即使连接不再可用,发送也会成功

send succeeds even when the connection is no longer available?

本文关键字:成功 连接 不再      更新时间:2023-10-16

我错过了什么?

我有非常简单的客户端和服务器。服务器超时使用recv(使用select) 3秒。然后shutdown s和close s作为插座和出口。

客户端连接到服务器,休眠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超时和成功发送消息的消息。

很抱歉,这个问题很长,而且可能很愚蠢。

一般情况下,您需要从套接字读取以使其注意到远程端已关闭连接。

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

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

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

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

我们的一些软件有这个问题——如果网络硬件在线路的某个地方失败了,它又恢复了——那么任何一端仍然认为套接字是有效的——没有keepalive探测——它会保持这种状态。

添加:查看socket选项(man setsockopt和man tcp) SO_KEEPALIVE

来自TCP手册页tcp_keepalive_intvl(整数;默认值:75;自Linux 2.4起)TCP keepalive探测之间的秒数。

   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.