5

I am writing a small 3 servers and 1 client program. the 2 servers send tcp messages and the last one sends upd datagrams using winsock2.

I am wondering if i can make simulanious recvfrom() by using threads (OpenMP or boost::threads) so that 2 threads listen from the same socket on the same port at the same time.

I am using VC++ 2010 on windows7.

Thank you for your help.

ezzakrem
  • 399
  • 5
  • 15
  • 2
    No, sockets aren't thread safe, you'll need a socket per thread, however, using the windows message loop you can notify one thread from another and vice versa, threads themselves can have message loops. – johnathan Dec 21 '12 at 02:34
  • 1
    "TCP messages" can be fragmented; TCP is a bytestream protocol. How would you deal with a message that's half received by one thread and half by another? The fact that it's not possible protects you against the more fundamental design error. Receive and reassemble the messages in one thread (that's not expensive anyway) and then dispatch entire messages for further processing to other threads. – MSalters Dec 21 '12 at 16:31
  • Thank you for your replies. I will consider using one socket per thread. – ezzakrem Dec 21 '12 at 17:00
  • 2
    Just a note, for TCP it usually doesn't make sense to concurrently call recv for the same socket, but for UDP it's perfectly fine. – cmeerw Dec 21 '12 at 17:43
  • @ezzakrem - two blocking calls on same thread is a rare condition (think how could one possibly issue recv, normally, from a thread when that thread is already stuck on recv) and MS documentation says the result would be undefined behavior as opposed to a well defined behavior of returning WSAEINPROGRESS in other cases. I am really interested to know how it works for you. – nanda Dec 25 '12 at 03:03
  • @nanda I tried to run two recvfrom() in two threads and it doesn't work because it must be WSAStartup() call in each task. – ezzakrem Dec 25 '12 at 16:07
  • @ezzakrem - WSAStartup needn't be called by each thread of a process. It is enough to successfully call it once in a process. Unless the two threads you mentioned are in different processes. – nanda Dec 27 '12 at 07:55

2 Answers2

10

Yes, sockets are thread-safe, however you have to be careful. One common pattern (when using blocking IO) is to have one thread receiving data on a socket and another thread sending data on the same socket. Having multiple threads receiving data from a socket is usually fine for UDP socket, but doesn't make much sense for TCP sockets most of the time. There is a warning in the documentation for WSARecv:

WSARecv should not be called on the same socket simultaneously from different threads, because it can result in an unpredictable buffer order.

But this usually isn't of any concern if you are using UDP and the protocol is stateless.

Also note that the WSAEINPROGRESS error code mainly applies to Winsock 1.1:

WSAEINPROGRESS: A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.

And the description of WSAEINPROGRESS further states:

Operation now in progress.

A blocking operation is currently executing. Windows Sockets only allows a single blocking operation—per- task or thread—to be outstanding, and if any other function call is made (whether or not it references that or any other socket) the function fails with the WSAEINPROGRESS error.

Note that this talks about a single blocking operation per-task or thread.

Furthermore there is an additional warning in the documentation for WSARecv:

Issuing another blocking Winsock call inside an APC that interrupted an ongoing blocking Winsock call on the same thread will lead to undefined behavior, and must never be attempted by Winsock clients.

But apart from those warnings you should be fine.

Update: to add some external references: alt.winsock.programming: Is socket thread-safe? and Winsock Programmer’s FAQ: Is Winsock thread-safe?

cmeerw
  • 7,176
  • 33
  • 27
  • Thank your for all these clear informations. So it's possible to run two wsarecv on the same socket in two threads for UDP. So i will get WSAEINPROGRESS error only if i call two blocking operations within the thread and not the whole precess. That's a great news since i have one recv() in each thread. Another question, sir. If i had two UDP sources, can i receive data from these sources using one thread and one socket (assuming that i use the same port)? – ezzakrem Dec 21 '12 at 17:14
  • For UDP you would normally use recvfrom to get the data and the peer's address and port number, so you can send the reply back to where the request came from. As there is no connection anyway it doesn't matter if each packet comes from the same sender or a different one. – cmeerw Dec 21 '12 at 17:41
1

Winsock allows only one blocking IO call on a socket. More than one blocking call from different thread would end up with "WSAEINPROGRESS" error. http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx#WSAEINPROGRESS.

If you want to make concurrent IO request you could try using asynchronous IO or overlapped IO (in windows parlance). But I guess you would want concurrent processing of data more than concurrent reading data. In which case you could have one thread issuing IO requests and others for processing.

nanda
  • 804
  • 4
  • 8
  • I very much doubt that - the documentation states "a single blocking operation—per- task or thread". The description for WSARecv further states "Issuing another blocking Winsock call inside an APC that interrupted an ongoing blocking Winsock call on the same thread will lead to undefined behavior, and must never be attempted by Winsock clients." – cmeerw Dec 21 '12 at 09:59
  • The documentation is talking about the case where APC "Asynchronous procedure call" would run on the same thread that issued the blocking call. As APC would get executed right before a thread is resumed - an user mode APC, does get a chance to run - and if it issues another blocking socket call the behavior is undefined. To give a code perspective, the documentation is talking about some code running on the same thread where recv or WSARecv is still executing, which is only possible when there are outstanding APC queued to the thread. – nanda Dec 21 '12 at 11:05
  • Yes, and the point is that more than one blocking call is only a problem on the same thread. but not when using multiple threads (one blocking call per thread is fine). – cmeerw Dec 21 '12 at 11:17