如果我在两个进程(或两个线程)之间共享一个套接字,并且在这两个进程中我尝试发送一条阻塞的大消息(大于下划线协议缓冲区),是否可以保证两个消息都将按顺序发送?或者消息可以在内核中交错?
我主要对 TCP over IP 行为感兴趣,但如果它根据套接字的协议而有所不同会很有趣。
您是在问,如果您write()
在同一个套接字上向 A 和 B 发送消息,A 是否保证在 B 之前到达?对于 SOCK_STREAM(例如 TCP)和 SOCK_SEQPACKET(几乎从未使用过)套接字,答案是肯定的。对于互联网上的 SOCK_DGRAM(即 UDP 数据包),答案是否定的:数据包可以由网络重新排序。在单个主机上,unix 域数据报套接字将(在我知道的所有系统上)保留顺序,但我不相信任何标准都能保证这一点,而且我确信存在边缘情况。
或者等等:也许你在问两个进程写入的消息是否不会混合?是的:单个系统调用(write/writev/sendto/sendmsg)总是以原子方式将它们的内容放入文件描述符中。但显然,如果您或您的库将写入拆分为多个调用,您将失去该保证。
对于 UDP,如果两个线程同时写入一个套接字句柄,两个消息将作为单独的数据报发送。如果数据包大于 MTU,可能会经历 IP 分段,但接收方将保留并正确重组生成的数据报。换句话说,您对 UDP 是安全的,除了与 UDP 相关的正常问题(数据报重新排序、数据包丢失等......)。
对于基于流的 TCP,我不知道。您的问题本质上是在问“如果两个线程尝试写入同一个文件句柄,该文件仍然清晰易读吗?” 我其实不知道答案。
您可以做的最简单的事情就是使用线程安全锁(互斥锁)来保护对套接字的发送/写入调用,这样只有线程才能一次写入套接字。
对于 TCP,我建议有一个专用线程来处理所有套接字 io。然后只需发明一种方法,使来自工作线程的消息可以异步排队到套接字线程以供其发送。套接字线程还可以处理 recv() 调用,并在套接字连接被远程端终止时通知其他线程。
如果您尝试在超过底层缓冲区大小的 STREAM 套接字上发送大消息,则几乎可以保证您将获得短写入 - 写入或发送调用将仅写入部分数据(尽可能多适合缓冲区)然后返回写入的数量,让您对剩余的数据进行另一次写入。
如果您在多个线程或进程中执行此操作,则每次写入(或发送)都会将消息的一小部分自动写入发送缓冲区,但后续写入可能以任何顺序发生,结果是大缓冲区发送将交错。
另一方面,如果您在 DGRAM 套接字上发送消息,则整个消息将被自动发送(作为单个第 4 层数据包,它可能被协议栈的较低层分段和重新组合),或者您将收到错误(EMSGSIZE
linux 或其他 UNIX 变体)