C socket原子非阻塞读取

时间:2016-09-08 19:16:31

标签: c sockets tcp nonblocking epoll

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

我不知道如何在服务器端实现“原子读取”。要解释我对“原子读取”的含义,请参阅此示例并使用简单的阻塞操作:

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

但是对于epoll +非阻塞操作,可能会发生这种情况:

  • 客户端和服务器都使用64K缓冲区。 (在应用程序级别。它们不会更改内核级别的套接字缓冲区。)
  • 客户端通过一次写操作写入12K数据。
  • 6K到达服务器
  • epoll告诉应用程序数据到达套接字
  • 应用程序使用非阻塞操作将6K读入缓冲区。
  • 重复读取时,返回EAGAIN / EWOULDBLOCK。

在这种情况下,读取不是“原子的”。不保证当使用单个写操作写入数据时,读取将以整体形式返回整个数据。

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

非常感谢!

1 个答案:

答案 0 :(得分:1)

TCP是基于流的,而不是面向消息的。即使在阻塞套接字的情况下,也无法保证应用程序发送的内容将一次性发送到线路上。 TCP将决定自己的方向。

因此,应用程序要做" atomic"读它的愿望。例如:

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

  

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

根据此信息,正在阅读的应用程序应采取措施。它应该轮询套接字,直到它接收到msg长度字节所指示的所有字节。然后才处理数据。

如果你需要" atomic"读取而非部分读取您可以在recv中使用MSG_PEEK标志。这不应该从套接字缓冲区中删除数据。应用程序查看套接字,根据返回值查看所需的数据是否在套接字缓冲区中。

  

ret = recv(sd, buf, MAX_CALL_DATA_SIZE, MSG_PEEK);