【问题标题】:recv() returning zero but cannot see any TCP disconnectionrecv() 返回零但看不到任何 TCP 断开连接
【发布时间】:2016-11-17 17:15:23
【问题描述】:

我正在使用下面的代码在套接字上接收。 recv() 返回接收到的零字节,这意味着连接已断开,但它看起来不像:

while( true )
{                  
    buffer = onGetBuffer( maxBytesToRecv );

    const int flags = MSG_WAITALL;

    errno = 0;
    const ssize_t bytesReceived = ::recv( descriptor() , buffer , maxBytesToRecv , flags );

    if ( bytesReceived > 0 ){
        processData( receivedTime , bytesReceived );   
    }
    else if ( bytesReceived < 0 ){
        const int eno = errno;
        if ( EINTR != eno ){
            // Log error code
        }
    }
    else{
        // Peer disconnected
        // ** Code is reaching here **
    }                   
}

我正在点击分支,暗示对等方已断开连接,因为bytesReceived 为零。我检查了一个 pcap 转储,但我们没有收到 TCP FIN 消息来关闭连接。

在此期间我运行了 strace(过滤网络消息)以检查我们自己没有关闭连接以及它记录的有问题的套接字:

sendto(16, "DATA"..., 88, MSG_NOSIGNAL, NULL, 0) = 88
sendto(16, "DATA"..., 88, MSG_NOSIGNAL, NULL, 0) = 88
sendto(16, "DATA"..., 5584, MSG_NOSIGNAL, NULL, 0) = 5584
sendto(16, "DATA"..., 5654, MSG_NOSIGNAL, NULL, 0) = 5654
sendto(16, "DATA"..., 5651, MSG_NOSIGNAL, NULL, 0) = 5651
sendto(16, "DATA"..., 5593, MSG_NOSIGNAL, NULL, 0) = 5593
sendto(16, "DATA"..., 5635, MSG_NOSIGNAL, NULL, 0) = 5635
sendto(16, "DATA"..., 5563, MSG_NOSIGNAL, NULL, 0) = 5563
sendto(16, "DATA"..., 5608, MSG_NOSIGNAL, NULL, 0) = 5608
sendto(16, "DATA"..., 5662, MSG_NOSIGNAL, NULL, 0 <unfinished ...>
sendto(16, "DATA"..., 5583, MSG_NOSIGNAL, NULL, 0) = 5583
sendto(16, "DATA"..., 5579, MSG_NOSIGNAL, NULL, 0) = 5579
sendto(16, "DATA"..., 3373, MSG_NOSIGNAL, NULL, 0) = 3373
sendto(16, "DATA"..., 201, MSG_NOSIGNAL, NULL, 0) = 201
recvfrom(16, "DATA"..., 7126, MSG_WAITALL, NULL, NULL) = 7126
recvfrom(16, "DATA"..., 6187, MSG_WAITALL, NULL, NULL) = 6187
recvfrom(16, "DATA"..., 7079, MSG_WAITALL, NULL, NULL) = 7079
recvfrom(16, "", 0, MSG_WAITALL, NULL, NULL) = 0

其中 16 是套接字的描述符。最后可以看到最终receive返回0。

如果我们自己没有断开连接并且我们没有收到来自另一端的 TCP FIN 来断开我们的连接。没有返回错误代码,errno 也是零。

如果没有断开连接,为什么recv() 返回零?或者我还能检查什么?

【问题讨论】:

  • 您已标记 [c],但由于将 recv() 引用为 ::recv(),因此您的代码无效 C。
  • recvfrom(16, "", 0, MSG_WAITALL, NULL, NULL) = 0 告诉我你请求了 0 个字节,而你得到了 0 个字节
  • 您是如何得出没有收到 TCP FIN 的结论的?
  • 是的,使用可能看起来确实是错误的。如果它完成了我认为您正在优化程序以使错误代码成为快速路径。
  • @JohnBollinger recv() 是 GNU c 库的一部分,您抱怨问题被标记为......... C.

标签: c linux sockets tcp glibc


【解决方案1】:

您的最后一次 recv 调用占用了 0 个字节。

来自 Linux 上的 recv 手册页:

如果从流套接字接收的请求字节数为 0,则也可能返回值 0。

作为对该程序的评论,在我看来maxBytesToRecv 是某种奇怪的全局或类成员。如果程序信任数据流来声明片段的大小,这看起来也可能是一个巨大的安全漏洞。

信任,但要验证。边界检查该长度值。处理零尺寸来解决这个问题,但也要检查尺寸是否太大。由于巨大的大小值和整数溢出,发生了许多许多漏洞。或者更糟糕的是,将无符号长度复制到有符号整数中,并将 0xFFFF 在某些地方视为 65535,在其他地方视为 -1。

【讨论】:

【解决方案2】:

最后一次调用 recv() 是否要求长度为零?

recvfrom(16, "", 0, MSG_WAITALL, NULL, NULL) = 0

如果您要求零字节,则您收到零字节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-08
    • 2016-02-23
    • 1970-01-01
    • 2018-07-07
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 2022-08-15
    相关资源
    最近更新 更多