C套接字原子非阻塞读取

C套接字原子非阻塞读取,c,sockets,tcp,nonblocking,epoll,C,Sockets,Tcp,Nonblocking,Epoll,我正在实现一个TCP/IP服务器应用程序,它在边缘触发模式下使用epoll,并执行非阻塞套接字操作。客户端使用简单的阻塞操作,而不使用epoll 我不知道如何在服务器端实现“原子读取”。为了解释我所说的“原子读取”的意思,请参阅以下简单阻塞操作的示例: 客户端和服务器都使用64K缓冲区。(在应用程序级。它们不会更改内核级套接字缓冲区。) 客户端通过一次写入操作写入12K数据 服务器读取它。在这种情况下,当缓冲区相同时,它总是读取整个12K。所以它不能只读其中的一半。这就是我所说的“原子” 但

我正在实现一个TCP/IP服务器应用程序,它在边缘触发模式下使用epoll,并执行非阻塞套接字操作。客户端使用简单的阻塞操作,而不使用epoll

我不知道如何在服务器端实现“原子读取”。为了解释我所说的“原子读取”的意思,请参阅以下简单阻塞操作的示例:

  • 客户端和服务器都使用64K缓冲区。(在应用程序级。它们不会更改内核级套接字缓冲区。)
  • 客户端通过一次写入操作写入12K数据
  • 服务器读取它。在这种情况下,当缓冲区相同时,它总是读取整个12K。所以它不能只读其中的一半。这就是我所说的“原子”
但在epoll+非阻塞操作的情况下,这可能发生:

  • 客户端和服务器都使用64K缓冲区。(在应用程序级。它们不会更改内核级套接字缓冲区。)
  • 客户端通过一次写入操作写入12K数据
  • 6K到达服务器
  • epoll告诉应用程序数据已到达套接字
  • 应用程序使用非阻塞操作将6K读入缓冲区
  • 重复读取时,它返回EAGAIN/ewoldblock
在这种情况下,读取不是“原子的”。不能保证,当使用单个写入操作写入数据时,读取将返回完整的数据

是否有可能知道数据何时是部分的?我知道一种解决方案是始终将数据大小附加到开头,或者另一种解决方案是始终关闭并重新打开连接,但我不想这样做:因为我认为内核必须知道,没有完整的“包”(这个单元如何称为BTW?)到达,因为它保证了阻塞操作的原子性


非常感谢

TCP是基于流的,而不是面向消息的。即使是在阻塞套接字的情况下,也不能保证应用程序发送的内容能够一气呵成。TCP将决定自己的路线

因此,由应用程序对其进行“原子”读取。例如:

应用程序协议应规定消息应以长度字节作为前缀。长度字节通知对等方感兴趣的应用程序数据的大小。当然,应用程序应该知道双字节长度指示器何时开始

[2字节消息长度][感兴趣的数据字节]

基于此信息,执行读取的应用程序应采取操作。它应该轮询套接字,直到收到msg长度字节指示的所有字节。只有这样才能处理数据

如果您需要“原子”读取而不是部分读取,您可以在
recv
中使用MSG_PEEK标志。这不会从套接字缓冲区中删除数据。应用程序窥视套接字,根据返回值查看套接字缓冲区中是否有所需数量的数据

ret=recv(sd、buf、最大调用数据大小、消息大小)


您通常需要在一个循环中写入所有I/O并维护您自己的缓冲区。如果您对套接字上的
read
调用没有返回
0
,那么您可以期望在事件循环中捕获更多的数据。在二进制传输中,通常会添加分隔符,例如传输开始和传输结束。在文本传输中,语法检查是您的朋友。如果您正在传输json字符串,您可以解析json字符串以确定它是否完整。
但是TCP/IP将您的数据保存在一起,这是通过一次写入发送的。或者那不是真的?
不,那不是真的。只保证顺序,不保证分段。TCP不提供内置消息边界,它们必须在应用程序级别实现。一条信息在传输过程中可以被分割成任意的片段。我会接受它,但我对此没有任何业力;)尽管您也可以添加这一点:“您关于阻塞情况的假设是不正确的:例如,服务器只能读取该阻塞读取的4K数据,然后返回。”NP!祝你好运,名声更大。记住要接受;-)如果答案有用,是否可以投票/接受?