TCP连接关闭时不发FIN包的奇怪行为分析
2016-04-11 17:25
555 查看
转载: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了。
可见,学习内核的协议栈是多么的重要啊!
一般情况下,当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了。
可见,学习内核的协议栈是多么的重要啊!
相关文章推荐
- Android网络编程概述
- IHttpModule接口
- 演练:创建和注册自定义 HTTP 模块
- HTTP 错误 404.3 - Not Found
- 严重: Failed to destroy end point associated with ProtocolHandler ["http-nio-8080"] java.lang.NullPoin
- TCP/IP 端口号大全
- 网络多任务下载,断点下载
- 网络流24题
- HTTP Error 500.19 错误 0x80070021
- hdu4289-网络流
- httpHelper
- win10网络共享失败提示共享依赖服务无法启动的解决办法
- 过程神经元网络
- COGS738 [网络流24题] 数字梯形(最小费用最大流)
- hdu5294-网络流+最短路
- apache httpd-2.4.18 安装教程
- iOS应用架构谈 网络层设计方案
- unix网络编程笔记(四)--IO复用
- hdu4975 -网络流
- 卷积神经网络反向推导算法