Winsock2是否线程安全?

5
我正在编写一个小型的3个服务器和1个客户端程序。其中2个服务器使用TCP发送消息,最后一个使用winsock2发送UDP数据包。
我想知道是否可以使用线程(OpenMP或boost :: threads)进行同时的recvfrom(),以便2个线程在同一时间监听来自同一端口的套接字。
我正在使用Windows7上的VC++ 2010。
感谢您的帮助。

2
不,套接字不是线程安全的,您需要为每个线程使用一个套接字。但是,使用Windows消息循环,您可以通知一个线程来自另一个线程,反之亦然,线程本身可以具有消息循环。 - johnathan
1
“TCP消息”可以被分割;TCP是一种字节流协议。如果一个消息被一半线程接收,另一半被其他线程接收,你会如何处理?事实上这样的情况不可能发生,从而保护你免受更基本的设计错误的影响。在一个线程中接收和重新组装消息(这并不昂贵),然后将整个消息发送到其他线程进行进一步处理。 - MSalters
2
只是一个提醒,对于TCP来说,通常不会同时为同一套接字调用recv,但对于UDP来说,这是完全可以的。 - cmeerw
@ezzakrem - 在同一线程上进行两个阻塞调用是一种罕见的情况(想象一下,当该线程已经被卡在recv上时,如何可能从线程中正常发出recv请求),而MS文档表示结果将是未定义的行为,而不是在其他情况下返回WSAEINPROGRESS的明确定义行为。我真的很想知道它对你是如何工作的。 - nanda
@nanda 我尝试在两个线程中运行两个recvfrom(),但是它不起作用,因为每个任务都必须调用WSAStartup()。 - ezzakrem
显示剩余2条评论
2个回答

10
是的,套接字是线程安全的,但您必须小心。一种常见模式(在使用阻塞IO时)是一个线程在套接字上接收数据,另一个线程在同一套接字上发送数据。对于UDP套接字,有多个线程从套接字接收数据通常是可以的,但对于TCP套接字大部分时间来说没有太多意义。在WSARecv文档中有一个警告:

不应该同时从不同的线程在同一套接字上调用WSARecv,因为它可能导致不可预测的缓冲区顺序。

但如果您正在使用UDP协议且该协议是无状态的,则通常不会有任何问题。

还要注意,WSAEINPROGRESS错误代码主要适用于Winsock 1.1:

WSAEINPROGRESS: 正在进行一个阻止的 Windows Sockets 1.1 调用,或者服务提供程序仍在处理回调函数。

WSAEINPROGRESS的描述进一步说明:

操作现在正在进行中。

当前正在执行一个阻塞操作。Windows Sockets只允许单个阻塞操作 - 每个任务或线程 - 处于未完成状态,如果进行任何其他函数调用(无论它是否引用该套接字或任何其他套接字),则该函数将失败并显示WSAEINPROGRESS错误。

请注意,这里谈论的是每个任务或线程的单个阻塞操作。

此外,在WSARecv文档中还有一个额外的警告:

在中断同一线程上正在进行的阻止Winsock调用的APC内发出另一个阻止的Winsock调用将导致未定义的行为,并且Winsock客户端绝不能尝试。

但除了这些警告之外,您应该没有问题。

更新:添加一些外部参考:

alt.winsock.programming: 套接字是否是线程安全的?Winsock程序员常见问题解答:Winsock是否线程安全?


感谢您提供这些清晰的信息。因此,在两个线程中在同一个套接字上运行两个wsarecv是可能的,适用于UDP。因此,只有在线程内调用两个阻塞操作而不是整个进程时,才会收到WSAEINPROGRESS错误。这是一个好消息,因为我每个线程中都有一个recv()。 另一个问题,先生。如果我有两个UDP源,是否可以使用一个线程和一个套接字(假设我使用相同的端口)从这些源接收数据? - ezzakrem
对于UDP,通常使用recvfrom获取数据和对等方的地址和端口号,以便可以将回复发送回请求来源的位置。由于没有连接,因此每个数据包来自同一发送者还是不同的发送者并不重要。 - cmeerw

1

Winsock在套接字上只允许一个阻塞IO调用。不同线程的多个阻塞调用将以“WSAEINPROGRESS”错误结束。http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx#WSAEINPROGRESS

如果您想进行并发IO请求,可以尝试使用异步IO或重叠IO(在Windows术语中)。但我猜您更希望并发处理数据而不是并发读取数据。在这种情况下,您可以有一个线程发出IO请求,其他线程用于处理。


我非常怀疑 - 文档中指出:“每个任务或线程只能执行单个阻塞操作”。WSARecv的描述进一步说明:“在被同一线程上正在进行的阻塞Winsock调用被APC中断后发出另一个阻塞Winsock调用将导致未定义的行为,Winsock客户端绝不能尝试这样做。” - cmeerw
文档讨论了APC“异步过程调用”在发出阻塞调用的同一线程上运行的情况。由于APC会在线程恢复之前执行 - 用户模式APC有机会运行 - 如果它发出另一个阻塞套接字调用,则行为是未定义的。从代码角度来看,文档讨论的是在仍在执行recv或WSARecv的同一线程上运行的某些代码,这仅在向线程排队了未完成的APC时才可能发生。 - nanda
是的,关键在于多个阻塞调用只有在同一线程上才会成为问题。但是在使用多个线程时(每个线程一个阻塞调用),这不是问题。 - cmeerw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接