【问题标题】:send succeeds even when the connection is no longer available?即使连接不再可用,发送也会成功?
【发布时间】:2011-12-30 15:13:24
【问题描述】:

我错过了什么?

我有非常简单的客户端和服务器。服务器使用recv 超时(使用select)3 秒。然后它shutdowns 和closes 连接套接字并退出。

客户端连接到服务器,休眠 30 秒并发送非常短的消息。服务器关闭套接字并退出后大约 27 秒发送。
send 不会失败..?为什么?为什么它不返回-1 错误?


请注意:我取消了所有对返回代码的检查并删除了所有日志,以尽可能缩短此内容。此外,我删除了所有包含,以使其更短。但它是一个真实的代码。

客户端代码:

int main( int argc, char* argv[] )
{
    addrinfo hints;
    memset(&hints, 0, sizeof hints); // make sure the struct is empty

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

    addrinfo *res;
    getaddrinfo( "127.0.0.1", "1313", &hints, &res );
    int nSocketFD = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
    assert( -1 != connect( nSocketFD, res->ai_addr, res->ai_addrlen) );
    freeaddrinfo( res ); // free the linked-list, we don't need it anymore

    sleep( 30 );
    if( send( nSocketFD, "bla", 4, 0 ) > 0 )
    {
        printf( "Message successfully sent!\n" );
    }   

    close( nSocketFD );

    return 0;
}

和服务器:

int main()
{
    addrinfo hints;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    addrinfo *res;  // will point to the results
    getaddrinfo( NULL, "1313", &hints, &res );
    int nSocketFD = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
    bind( nSocketFD, res->ai_addr, res->ai_addrlen );
    freeaddrinfo( res ); // free the linked-list
    listen( nSocketFD, 1 );

    sockaddr_storage their_addr;
    socklen_t addr_size = sizeof( sockaddr_storage );
    int nAcceptedSocket = accept( nSocketFD, (sockaddr*)&their_addr, &addr_size );
    assert( -1 != nAcceptedSocket );

    fd_set fds;
    FD_ZERO( &fds );
    FD_SET( nAcceptedSocket, &fds );
    timeval tv; 
    tv.tv_sec = 3;
    tv.tv_usec = 0;

    if( 0 == select( nAcceptedSocket + 1, &fds, NULL, NULL, &tv) )
    {   
        printf( "recv timeout! Exiting..\n" );
        shutdown( nSocketFD, SHUT_RDWR );
        close( nSocketFD );
        shutdown( nAcceptedSocket, SHUT_RDWR );
        close( nAcceptedSocket );
        return 1;
    }   
    assert( false );
    return 0;
}

当我执行它时,我会看到 recv 超时和成功发送消息的消息。

很抱歉这个冗长且可能很愚蠢的问题。

【问题讨论】:

    标签: c++ c sockets unix network-programming


    【解决方案1】:

    一般来说,你需要从一个套接字中读取,让它注意到远程端已经关闭了连接。

    来自send 的手册页(只是write,但带有标志):

    No indication of failure to deliver is implicit in a send().  Locally
    detected errors are indicated by a return value of -1.
    

    【讨论】:

    • @yi_H select et al 将在连接关闭时返回一个套接字是可读的,但您仍然必须从实际的套接字中读取才能得到该错误。
    • OMG (facepalm)(facepalm)(facepalm) 我阅读了sendrecv 的手册页,不知何故我想,就像recvsend 将返回0,在如果连接从另一端关闭:XI 认为今天 10 小时的工作就足够了。非常感谢!
    • 有 (E)POLLERR/HUP。并且有 RDHUP 用于半关闭。
    • @yi_H POLLHUP 不会在所有系统或所有文件描述符类型的关闭时发送。例如,它不是在 Linux 中为套接字发送的。找出连接是否已关闭的唯一可靠方法是从套接字读取
    【解决方案2】:

    您没有关闭客户端中的套接字 - 所以这是一个有效的套接字。来自 send() 的手册页

    No indication of failure to deliver is implicit in a send().  Locally detected errors are indicated by a return value of -1.
    

    我们的某些软件存在这个问题 - 如果网络硬件在某处出现故障并再次恢复正常 - 那么任一端仍然认为套接字是有效的 - 并且没有 keepalives 探测 - 它将保持这种状态。

    添加: 看看套接字选项(man setsockopt 和 man tcp)SO_KEEPALIVE

    来自 TCP 手册页 tcp_keepalive_intvl(整数;默认值:75;从 Linux 2.4 开始) TCP keep-alive 探测之间的秒数。

       tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
              The  maximum  number  of  TCP  keep-alive  probes  to  send before giving up and killing the connection if no response is obtained from the other end.
    
       tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
              The number of seconds a connection needs to be idle before TCP begins sending out  keep-alive  probes.   Keep-alives  are only  sent when the SO_KEEPALIVE socket option is enabled.  The default value is 7200 seconds (2 hours).  An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval of 75  seconds  apart)  when  keep-alive is enabled.
    
              Note that underlying connection tracking mechanisms and application timeouts may be much shorter.
    

    【讨论】:

    • 我不需要关闭客户端中的套接字。我需要检测到,服务器已经消失。还是谢谢
    • 对不起 - 应该更清楚 - 我的意思是套接字如果在客户端有效 - 所以它会很乐意接受数据发送
    • 是的,你完全正确。我只是想我读到send 返回0,以防万一连接从另一端关闭。我只是将recv 手册页与send 混合在一起:) 所以,我的问题通过添加recv(非阻塞)来解决,只是为了检查连接是否仍然可用。我不知道这是否是最好的解决方案,但它绝对解决了我的问题:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 1970-01-01
    • 1970-01-01
    • 2021-05-30
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多