zoukankan      html  css  js  c++  java
  • tcp 关闭socket 不发 FIN(RST)

    转自:http://blog.chinaunix.net/uid-10106787-id-3172066.html

    一般情况下,当TCP连接主动关闭时,会向对端发送一个FIN,对端会获得一个读事件,调用read时返回0,表示读到一个EOF,读结束。然而,在有的时候却不是这样的,接下来将讨论一下。
     
    首先是一个简单的服务器程序,accept()后睡眠5s钟,然后关闭连接。
    int main(void)
    {
        int fd;
    
        fd = socket(AF_INET, SOCK_STREAM, 0);
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof servaddr);
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(9001);
        servaddr.sin_addr.s_addr = 0;
    
        const int on = 1;
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
    
        bind(fd, (struct sockaddr *) &servaddr, sizeof servaddr);
    
        listen(fd, 10);
    
        int clifd = accept(fd, NULL, NULL);
    
        sleep(5);
    
    //    char buf[1024];
    //    read(clifd, buf, sizeof buf); 
    
        close(clifd);
    
        close(fd);
    
        return 0;
    }
    

      下面是一个简单的客户端程序,连接成功后发送1024字节的数据,然后调用read()

    int main(void)
    {
        int fd, ret;
        
        fd = socket(AF_INET, SOCK_STREAM, 0);
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof servaddr);
    
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(9001);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        connect(fd, (SA *) &servaddr, sizeof servaddr);
    
        char buf[1024];
    
        if (write(fd, buf, sizeof buf) != sizeof buf)
            err_sys("write error");
    
        if ((ret = read(fd, buf, sizeof buf)) < 0)
            err_sys("read error");
    
        fprintf(stderr, "Read %d Bytes
    ", ret);
    
        if (close(fd) < 0)
            err_sys("close error");
    
        return 0;
    }
    

      运行结果如下:

    read error : Connection reset by peer
    

      

    可见server在close时向client发送的不是FIN,而是RST,为什么会这样呢?我们从内核中找答案。
     
    见 net/ipv4/tcp.c 中的 tcp_close() 函数,
    /* As outlined in RFC 2525, section 2.17, we send a RST here because
         * data was lost. To witness the awful effects of the old behavior of
         * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
         * GET in an FTP client, suspend the process, wait for the client to
         * advertise a zero window, then kill -9 the FTP client, wheee...
         * Note: timeout is always zero in such a case.
         */
        if (data_was_unread) {
            /* Unread data was tossed, zap the connection. */
            NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
            tcp_set_state(sk, TCP_CLOSE);
            tcp_send_active_reset(sk, sk->sk_allocation);
        }
    

      

    代码里面写得很清楚,如果你的接收缓冲区中还有数据,协议栈就会发送RST而不是FIN。
     
    我们再来验证一下,在server中先调用read()清空读缓冲区后再close(),此时发现client会收到FIN了。
     
    可见,学习内核的协议栈是多么的重要啊!
  • 相关阅读:
    菜根谭#308
    菜根谭#307
    菜根谭#306
    菜根谭#305
    菜根谭#304
    菜根谭#303
    菜根谭#302
    菜根谭#301
    菜根谭#300
    菜根谭#299
  • 原文地址:https://www.cnblogs.com/yorkyang/p/7651806.html
Copyright © 2011-2022 走看看