1

As briefly introduced in the question title, I am trying to design and implement a server application able to let clients share audio contents between themselves. In order to achieve that, I decided to let the server have the following features:

  1. it should listen for incoming TCP connections using a dedicated thread.
  2. it should use a defined number of threads to manage each client TCP request independently. These latter can mainly be of two different types:
    1. entry requests (a client would start to share audio with others)
    2. exit requests (a client would stop to share audio with others)
  3. it should dedicate a defined number of threads in order to send and receive contents respectively to and from connected clients. Audio content should be shared using the UDP protocol.
  4. it should have memory of the clients connected to the network. In this way, server should be able to redirect a received audio stream to all the other connected clients.

Taking these features into account, the code at the actual state has been arranged in the following way:

  1. an AudioStreamServer class has been implemented. It represent the whole server entity.
  2. AudioStreamServer has a TCPListener class which represent the main listening thread, a template class ThreadManager which is able to manage thread activities. These latter can be TCPConnections or UDPConnections.
  3. TCPListener, TCPConnection and UDPConnection are like/derive from Thread which represent the basic thread features. It is nothing but a little wrapper of the std::thread class.

But here are some issues and doubts I am facing:

  1. is it a good strategy to let TCPListener, TCPConnection and UDPConnection classes inherit from Thread? If not, would it be better to let the Thread class be a template inside which TCPListener, TCPConnection, etc. are started as new thread using their () operator (treating them as functions)?
    Here is a brief example of the second option:

    template <typename T>
    class Thread {
    public:
      Thread(): should(false), isRunning(false), t() {}
      ~Thread() {}
      void start() {
        if (!(isRunning)) {
          assert(!(should));
          should = true;
          t = std::thread(T(), std::ref(*this));
          isRunning = true;
        }
      }
      void stop() { // Well, should be uncallable from inside the thread...
        if (isRunning) {
          assert(should);
          should = false;
          if (t.joinable()) {
            t.join();
          }
        }
        isRunning = false;
      }
      void requestStop() {
        if (isRunning) {
          assert(should);
          should = false;
        }
      }
      bool shouldRun() const {
        return should;
      }
    private:
      bool should;
      bool isRunning;
      std::thread t;
    };
    
    class TCPListener {
    public:
      TCPListener() {}
      ~TCPListener() {}
      void operator()(Thread<A>& myThread) {
        while (myThread.shouldRun()) {
          print();  // Do something...
          sleep(1);
        }
      }
      virtual void print() {
        std::cout << "I'm listening!" << std::endl;
      }
    };
    
    int main(int argc, char const *argv[]) {
      Thread<TCPListener> a;
      a.start();
      sleep(10);
      a.stop();
      return 0;
    }
    
  2. since, for instance, TCPListener should communicate with ThreadManager<TCPConnection> and TCPConnection, in the current state of the code this kind of communication has been made possible by constructing the object with a reference to the others. Is this a good way to let classes communicate between themselves? Am I actually coupling them, making all the code more difficult to maintain? Interface classes could be a better solution? (for example, TCPListener - derived from ITCPListener - communicates with IThreadManager<ITCPConnection> and ITCPConnection)

  • n.b. should needs to be wrapped with synchronization primitives or you need to use an event, signal or mutex! – esoterik Feb 19 at 1:36
  • Mutex should be used when a variable (or anyway, allocated memory) is shared between different threads, is it right? – rudicangiotti Feb 20 at 19:21
  • @rudicangiotti It's a little bit more complicated than that, but you're right. If variable is accessed by different threads it needs to be protected by mutex, be an atomic variable or both. – BJovke Feb 21 at 10:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.