在 C 或 C++ 中的同一个套接字上同时读取和写入
Posted
技术标签:
【中文标题】在 C 或 C++ 中的同一个套接字上同时读取和写入【英文标题】:simultaneously read and write on the same socket in C or C++ 【发布时间】:2012-10-12 20:59:27 【问题描述】:我正在实现一个简单的服务器,它接受单个连接,然后使用该套接字同时从读写线程读取和写入消息。 在linux上的c/c++中同时读取和写入同一个套接字描述符的安全和简单的方法是什么? 我不需要担心多个线程从同一个套接字读取和写入,因为将有一个专用的读取和一个专用的写入线程写入套接字。
在上述场景中,是否需要任何类型的锁定?
以上场景是否需要非阻塞socket?
是否有任何开源库可以在上述情况下有所帮助?
【问题讨论】:
【参考方案1】:在上述场景中,是否需要任何类型的锁定?
无。
以上场景是否需要非阻塞socket?
您可能担心的一点 - 已建立连接上的读取和写入线程 - 如果您对这些线程坐在那里等待完成感到高兴,则不需要非阻塞。这通常是您使用线程而不是选择、轮询或异步操作的原因之一......也让代码更简单。
如果接受新客户端的线程乐于阻止对 accept()
的调用,那么你也可以。
不过,如果您的程序发展到可以处理多个客户端并且需要定期进行一些内务处理,那么您可能希望牢记 TCP 服务器的一个微妙问题。使用带有超时的select
语句来检查侦听套接字的可读性是很自然和诱人的——这表明客户端连接尝试——然后是accept
连接。那里有一个竞争条件:客户端连接尝试可能在select()
和accept()
之间中断,在这种情况下,如果侦听套接字不是非阻塞的,accept()
将阻塞,这会阻止及时返回到@987654327 @ 循环并暂停周期性的超时处理,直到另一个客户端连接。
是否有任何开源库可以在上述情况下有所帮助?
有数百个用于编写基本服务器的库,但最终您所要求的很容易在操作系统提供的 BSD 套接字或其 Windows 混蛋上实现。
【讨论】:
+1 用于提及竞争条件和使用线程与选择或轮询的设计选项 @Tony D 好建议。从增强的角度来看,如果他将来计划使用 OpenSSL,那么他的架构可能会发生变化。他不能在同一个 SSL* 上同时读写。 @pathygeek:有趣——我没有做过 SSL 编程,所以不知道,但绝对值得记住。干杯。 "那里存在竞争条件:客户端连接尝试可能在 select() 和 accept() 之间中断,在这种情况下,即使侦听套接字不是非阻塞的,accept() 也会阻塞,并且这可能会阻止及时返回到 select() 循环并暂停周期性的超时处理,直到另一个客户端连接。” --> 这是写你在这里写的更好的方式吗? @TonyDelroy 我刚刚意识到我读错了您的原始答案,感谢您的帮助。【参考方案2】:套接字是双向的。如果您曾经真正剖析过以太网或串行电缆或查看过它们的低级硬件接线图,您实际上可以看到“TX”(发送)和“RX”(接收)线的不同铜线。用于发送信号的软件,从设备控制器到“套接字”的大多数操作系统 API,都反映了这一点,它是大多数系统(例如 Linux)上套接字和普通管道之间的关键区别。
要真正充分利用套接字,您需要: 1) 异步 IO 支持,它使用 IO 完成端口、epoll() 或一些类似的异步回调或事件系统在数据进入套接字时“唤醒”。然后,这必须调用最低级别的“ReadData”API 以从套接字连接中读取消息。 2)支持低级写入的第二个 API,一个将字节推送到套接字上的“WriteData”(传输),不依赖于“ReadData”逻辑所需的任何内容。请记住,即使在硬件级别,您的发送和接收也是独立的,因此不要在此级别引入锁定或其他同步。 3) Socket IO 线程池,盲目地对从套接字读取或将写入套接字的数据进行任何处理。 4) PROTOCOL CALLBACK:套接字线程具有智能指针的回调对象。它处理位于基本套接字连接之上的任何 PROTOCOL 层——例如将数据 blob 解析为真正的 HTTP 请求。请记住,套接字只是计算机之间的数据管道,通过它发送的数据通常会以一系列片段(数据包)的形式到达。在像 UDP 这样的协议中,数据包甚至不是按顺序排列的。低级别的“ReadData”和“WriteData”将从它们的线程回调到这里,因为它是内容感知数据处理真正开始的地方。 5) 协议处理程序本身需要的任何回调。对于 HTTP,您将原始请求缓冲区打包成漂亮的对象,然后交给真正的 servlet,后者应该返回一个可以序列化为符合 HTTP 规范的响应的漂亮响应对象。
注意基本模式:如果您希望充分利用套接字上的双向异步 IO,则必须使整个系统从根本上异步(“回调洋葱”)。同时读取和写入套接字的唯一方法是使用线程,因此您仍然可以在“写入器”和“读取器”线程之间进行同步,但我只会在协议或其他考虑迫使我手时才这样做。好消息是,您可以使用高度异步处理的套接字获得出色的性能,坏消息是,以稳健的方式构建这样的系统是一项艰巨的工作。
【讨论】:
'双向' 是不够的。半双工仍然是双向的,但不是同时的。它需要是全双工的才能满足 OP 的要求。 TCP/IP 是全双工的,也是双向的。 感谢您特别提及单独的物理行。这有助于我概念化它是如何以及为什么是双向和全双工的。【参考方案3】:您不必担心。一个线程读取和一个线程写入将按您的预期工作。套接字是全双工的,所以你可以边写边读,反之亦然。如果您有多个作家,您将不得不担心,但事实并非如此。
【讨论】:
简短回答,我喜欢。使用“担心多个作家”,您的意思是在同一频道上同时进行写作会使事情变得一团糟,对吗?所以对于多个写入线程,必须应用同步方法。以上是关于在 C 或 C++ 中的同一个套接字上同时读取和写入的主要内容,如果未能解决你的问题,请参考以下文章
C++ read()-ing 从一个套接字到一个 ofstream