2

I'm developing the client-server Winsock app (Visual C++) that should transmit the various kind of data (video stream, audio stream, service notifications, etc.) over the network. I know that the cleaner approach would be to use separate ports on separate threads for each individual data type (I call it "stream" here). But that would require occupying at least 5 different ports that would get problematic for some network infrastructures (firewall port forwarding, etc.).

So I'm trying to implement one-port connection (TCP), only one socket will be used to transmit different streams. The individual packets will have the info in header that will denote which stream it belongs to, total message size to be expected, etc. Say I have 5 different streams. I'm planning to use 5 threads to call send() of the same socket. Is this safe? I know the packets from different streams will arrive mixed but if I include necessary meta information in each sent packet, it could be reconstructed properly on the other side, right?

But the real problem is the receiving end. While it's probably OK to call send() from multiple threads on the same socket (though I'm not sure and need your confirmation!), calling recv() from multiple threads would not make much sense. So I should use one blocking recv() from one thread. But then, based on the packet header (identifying which stream the particular packet belongs to) I should fork the processing in different threads. Video stream should be processed by one thread, sound stream - by another, etc. But I don't quite know how to do this - how to fork the data processing from the receiving thread. Performance is a top priority so processing all streams by one thread can't be considered.

So to sum it up, I have three questions:

  • Is it OK to call send() for same socket from multiple threads? (assuming there will be info in packet header about which sender thread (i.e. subsystem) it belongs).
  • Having one blocking socket on the receiving end, calling recv() in loop from a single thread, how to "fork" the received packets of different logical streams to different worker threads?
  • What would be your additional recommendations for implementing multiple-stream transfer through one port?

P.S.: Afaik, there's no way to use one port by multiple sockets, correct?

It's Windows platform, Winsock2, Visual C++ (if you could provide platform-specific hints that would be awesome).

= UPDATE =

  • When you say to "lock socket", you mean serialize the access to send() function? e.g. with Critical Section?

  • As for receiving end... I think I'll assemble messages (I call "message" the logical complete data structure, e.g. video frame, or sound sample, etc.) when recv()-ing them in loop (buffering messages from individual streams into individual buffers), and then I just pass the assembled messages (when one will be fully received), to forking threads. Now that is the question - how to pass them. The one way I've thought of is Event Objects: SetEvent() from receiver thread to trigger WaitForSingleObject() (which is in some loop) in different threads. Can you advice if this is the acceptable solution? Can you suggest anything better? Aren't there any faster solutions to do this ("trigger" another thread of the same application) than Event Objects? And how to pass the data?

TX_
  • 23
  • 1
  • 4

2 Answers2

3
  • Is it OK to call send() for same socket from multiple threads? (assuming there will be info in packet header about which sender thread (i.e. subsystem) it belongs).

Of course you can call it from multiple threads. But remember that you have to put a lock on the socket to synchronize it. Otherwise you might end up getting information written inconsistent.

  • Having one blocking socket on the receiving end, calling recv() in loop from a single thread, how to "fork" the received packets of different logical streams to different worker threads?

How about writing a BeginRecv and EndRecv as similar to boost asios library (or switching to Boost Asio. It is plattform independent!). When you have recieved a complete message you can send it away. I guess you would like to put that somewhere else as to continue recieving from the network? You could just use CreateThread and send your data via the lpParameter parameter and it would be someone elses problem than recv. There wouldn't be much sence in forking it prior to your data being downloaded because you still need to download the complete package before the next one is able to be.

  • What would be your additional recommendations for implementing multiple-stream transfer through one port?

Wouldn't know much about it, but I have written an answer earlier regarding Winsock where I describe how uncertain recv() is. Don't forget to synchronize as to make sure that one package is written before the next one is.

When you say to "lock socket", you mean serialize the access to send() function? e.g. with Critical Section?

Yes indeed. You can do this pretty by using for instance RAII for the lock (then it would be automagically deleted/unlocked on function return). But yes, use a sendData function where you first lock an object and then send your data.

Now that is the question - how to pass them.

Well , You can just pass it as a new thread.

DWORD WINAPI myVideoProcessor(LPVOID theData){
    StructForKeepingVideoData* data = dynamic_cast<StructForKeepingVideoData*>(theData);
    // process the data that is passed in theData
    ...
}
...
void ReceiveData(){
    while (true){
        ...
        char buffer[SIZE];
        recv(mySocket, buffer, SIZE, 0);
        StructForKeepingVideoData* data = new StructForKeepingVideoData(buffer);

        HANDLE mId = CreateThread(NULL, 0, myVideoProcessor, data, 0, NULL);
        // now, the video processing will be done somewhere else. let's continue receiving data!
    }
}

I am kind of hazy here.. But I think you should check for constructions such as auto_ptr or shared_ptr when transferring the data - this will help you with the destruction of the data.

The one way I've thought of is Event Objects: SetEvent() from receiver thread to trigger WaitForSingleObject() (which is in some loop) in different threads. Can you advice if this is the acceptable solution?

Events would work as well, but it is not something i have ventured in to. Then you would just keep your thread going all the time instead of starting when you have data to process. Remember that you have to keep the data transfer synchronized as well. If you have some sort if queue you have to make sure that writes and reads are done after one another.

Can you suggest anything better? Aren't there any faster solutions to do this ("trigger" another thread of the same application) than Event Objects? And how to pass the data?

Wouldn't know of anything faster. But to pass the data, either do it when you create the thread or use some sort of queue that you keep synchronized.

Hope this helps.

Community
  • 1
  • 1
default
  • 11,485
  • 9
  • 66
  • 102
  • Thanks for your answer. Could you please view my update (edit of main question) and express your opinion? – TX_ Jun 13 '11 at 23:07
  • @Default. Now I have read your answer about "Winsock considerations" and just though... If - as you say - **recv()** can retrieve more than one chunk with one call, there is no way to mark each chunk with stream metainfo when **send()ing** them right? i.e. If I use 3 **send()s** but the all 3 buffers will be returned by one **recv()** call as a contiguous buffer, there's no way to individually process the chunks right? I thought I could include one-byte "stream id" in each and every chunk that I send with **send()** function, but this behavior of **recv()** makes this technique impossible. – TX_ Jun 14 '11 at 11:37
  • @Default. So there's no way to "interleave" messages of different streams - calling **send()** from different threads won't make a sense (even if I serialize actual **send()** call) because more than one chunk can be retrieved as one buffer. So the only way left is to send each message completely and only then process next message (of same or other stream). This will lock other threads if one thread is sending big message (several MB), so I have to split my messages into smaller ones in the upper layers of my application, instead of transport layer. That's sad :( – TX_ Jun 14 '11 at 11:38
  • @Default. Can you think of any workaround that could make the original intention possible (when multithreading could make any logical sense)? – TX_ Jun 14 '11 at 11:39
  • @TX_ yes, but you still need to make sure that the chunks of data is not mixed and matched with each other. that's why you need to synchronize the sends. yup, `recv()` is a tricky one. that's why you have Boost Asio that can help you with that. Check out BeginRecv and EndRecv in the Asio Library. you can of course call send from multiple threads. but you'll have to include the length of the message so that you can divide it on the receiving end. send will make sure to send all the data, but make sure you don't hit the TCP Zero Window (google it). My tip is: try a prototype and learn from... – default Jun 14 '11 at 12:12
  • @Default. OK, I'm not gonna complicate this further, and I don't really want to use Boost for just this functionality, so I'll probably serialize access to my whole message-sending routine (I'll guard it with critical section markers), so that whole messages will be sent before processing next one. I'll lower the size of each individual message (e.g. send video frame with multiple messages). Thanks for your answers once again! – TX_ Jun 14 '11 at 12:54
0

Is it OK to call send() for same socket from multiple threads?

Sure, providing you lock the socket up. If I was doing this, I would queue up all the outgoing protocol units to one thread and send with that, possibly using a priority queue to enable some guarantee of prompt delivery to those streams that need it.

how to "fork" the received packets of different logical streams to different worker threads?

Your protocol is going to have to do that. Assemble a video/audio/whatever protocol-unit from the incoming byte-stream and queue the PDU to an appropriate handler thread.

Windows - IO completion ports at the server if you need high performance which, if you are attempting to stream video, you will.

Rgds, Martin

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • Martin, thanks for your answer. Could you please view my update (edit of main question) and express your opinion? – TX_ Jun 13 '11 at 23:07
  • You could use a CS to lock the socket, yes. How to pass them: on a producer-consumer queue. Once you have assembled a complete PDU, push it, (it's reference), to a thread and allocate/depool another one for the next PDU. Try not to use a single event to implement your producer-consumer queue. Is there a thread-safe queue in VC++? If not, make one with a CS protecting the push/pop. Then, after assembling PDU, push it onto the queue and signal a semaphore. In the handler thread, wait on the semaphore and then pop the PDU from the queue. – Martin James Jun 14 '11 at 18:06