tcp 关闭socket 不发 FIN(RST)
阅读原文时间:2024年07月01日阅读:1

转自: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\\n", 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了。

可见,学习内核的协议栈是多么的重要啊!

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章