在同一个套接字上并行调用send/recv是否有效?

143
  1. 我们能否在同一个套接字上从一个线程调用send,而从另一个线程调用recv?
  2. 我们能否在同一个套接字上并行地从不同的线程调用多个send?

我知道一个好的设计应该避免这种情况,但是我不清楚这些系统API会如何运作。我也找不到相关的文档。

如果有任何指导方向的建议将会很有帮助。


4
为什么你认为这样做是一种不好的做法?我感觉没问题,因为你可以在不同的线程中侦听并接收。 - TheMathNoob
3个回答

107

POSIX将send/recv定义为原子操作,因此假设您谈论的是POSIX send/recv,那么是的,您可以从多个线程同时调用它们,而且一切都能正常工作。

这并不一定意味着它们将并行执行--在多个发送的情况下,第二个发送可能会阻塞,直到第一个完成。 您可能不会注意到这一点,因为一旦发送将其数据放入套接字缓冲区中,发送就会完成。

如果您正在使用SOCK_STREAM套接字,尝试以并行方式执行任务的效果可能不那么实用,因为send/recv可能仅发送或接收消息的一部分,这意味着事情可能会被拆分。

在SOCK_STREAM套接字上进行阻塞式的send/recv只会阻塞,直到它们发送或接收至少1个字节,因此阻塞和非阻塞之间的差异没有用处。


4
SOCK_DGRAM套接字被记录为“保留消息边界”,但这并不太清楚。从查看Linux内核源代码中,您至少可以看到每个发送和接收至少对于UDP都原子地处理单个数据包。 - Chris Dodd
3
@Kedar:不确定你的意思。 send 函数在数据被放入发送缓冲区后立即返回,并且数据异步地通过网络堆栈发送到网络上。因此,如果有一个线程发送数据并且另一个线程接收数据,很可能在接收线程收到第一个数据包之前,发送线程会发送多个数据包。这是完全异步的,而不是同时进行的。 - Chris Dodd
7
@ChrisDodd,您能否提供“POSIX将send/recv定义为原子操作”的链接? - suitianshi
4
POSIX 1003.1c 标准文档列出了在多线程环境下可以安全调用的所有 1003.1 函数和不支持的函数。很遗憾,我不知道有任何免费在线副本可用。 - Chris Dodd
2
@ChrisDodd 我已经在http://www.unix-systems.org/version4/上找到了拷贝,我能看到第7.1章节列出了系统接口表但没有看到函数被列为原子操作。不是怀疑你,但你能否分享/编辑你的答案并在文档中证明你的观点? - user153882
显示剩余5条评论

24

套接字描述符属于进程而不是特定的线程。因此,在不同线程中发送/接收来自同一套接字是可行的,操作系统将处理同步。

然而,如果发送/接收的顺序在语义上具有重要意义,则您自己(或您的代码)必须确保不同线程之间操作的正确排序 - 正如始终与线程一样。


3
我不明白并行接收如何可能有任何作用。如果您有一个3字节的消息,一个线程可以获取前2个字节,另一个线程可以获取最后一个字节,但您无法知道哪个是哪个。除非您的消息只有一个字节长,否则您无法可靠地使用多个线程接收来使任何事情正常工作。
多个发送可能会起作用,如果您在单个调用中发送整个消息,但我不确定。可能会互相覆盖。这样做肯定不会带来任何性能上的好处。
如果多个线程需要发送,则应实现同步消息队列。有一个线程执行实际发送,从队列中读取消息,并让其他线程将整个消息加入队列。接收也可以采用同样的方法,但接收线程必须知道消息的格式,以便可以正确地反序列化它们。

10
如果你正在使用SOCK_DGRAM套接字,每次接收操作(recv)只会接收到一个数据报(datagram),它永远不会在多个接收操作中分割。 - Chris Dodd
2
@noah,我同意并行接收没有任何作用。这就是为什么我没有问它的原因。 我的问题是并行发送/接收然后多重并行发送。 你的答案确实给了并行发送的见解。感谢你。 - Jay
2
@Chris 很好的观点。我假设了TCP。@Jay 你可能需要澄清一下问题,“我们可以同时调用send / recv”听起来像是你想要并行接收。 - noah

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