TCP链接主动关闭不发fin包奇怪行为分析

标签: Erlang探索 网络编程 fin rest TCP | 发表时间:2011-07-01 17:40 | 作者:Yu Feng Eric
出处:http://blog.yufeng.info

原创文章,转载请注明: 转载自Erlang非业余研究

本文链接地址: TCP链接主动关闭不发fin包奇怪行为分析

问题描述:
多隆同学在做网络框架的时候,发现一条tcp链接在close的时候,对端会收到econnrest,而不是正常的fin包. 通过抓包发现close系统调用的时候,我端发出rst报文, 而不是正常的fin。这个问题比较有意思,我们来演示下:

$ erl
Erlang R14B03 (erts-5.8.4) 1 [64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8.4  (abort with ^G)
1> {ok,Sock} = gen_tcp:connect("baidu.com", 80, [{active,false}]).
{ok,#Port<0.582>}
2> gen_tcp:send(Sock, "GET / HTTP/1.1\r\n\r\n").
ok
3> gen_tcp:close(Sock).
ok

我们往baidu的首页发了个http请求,百度会给我们回应报文的,我们send完立即调用close.

然后我们在另外一个终端开tcpdump抓包确认:

$ sudo tcpdump port 80 -i bond0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bond0, link-type EN10MB (Ethernet), capture size 96 bytes
17:22:38.246507 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: S 2228211568:2228211568(0) win 5840 <mss 1460,sackOK,timestamp 2607833238 0,nop,wscale 7>
17:22:38.284602 IP 220.181.111.86.http > my031089.sqa.cm4.tbsite.net.19500: S 3250338304:3250338304(0) ack 2228211569 win 8190 <mss 1436>
17:22:38.284624 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: . ack 1 win 5840
17:22:52.748468 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: P 1:19(18) ack 1 win 5840
17:22:52.786855 IP 220.181.111.86.http > my031089.sqa.cm4.tbsite.net.19500: . ack 19 win 5840
17:22:52.787194 IP 220.181.111.86.http > my031089.sqa.cm4.tbsite.net.19500: P 1:179(178) ack 19 win 5840
17:22:52.787203 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: . ack 179 win 6432
17:22:52.787209 IP 220.181.111.86.http > my031089.sqa.cm4.tbsite.net.19500: P 179:486(307) ack 19 win 5840
17:22:52.787214 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: . ack 486 win 7504
17:23:01.564358 IP my031089.sqa.cm4.tbsite.net.19500 > 220.181.111.86.http: R 19:19(0) ack 486 win 7504
...

我们可以清楚的看到 R 19:19(0) ack 486 win 7504,发了个rst包,通过strace系统调用也确认erlang确实调用了close系统调用。
那为什么呢? @淘宝雕梁,tcp协议栈专家回答了这个问题:

在net/ipv4/tcp.c:1900附近

...
 /* 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.
我们再来验证一下:

$ erl
Erlang R14B03 (erts-5.8.4) 1 [64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8.4  (abort with ^G)
1> {ok,Sock} = gen_tcp:connect("baidu.com", 80, [{active,false}]).
{ok,#Port<0.582>}
2> gen_tcp:send(Sock, "GET / HTTP/1.1\r\n\r\n").
ok
3> gen_tcp:recv(Sock,0).
{ok,"HTTP/1.1 400 Bad Request\r\nDate: Fri, 01 Jul 2011 09:24:37 GMT\r\nServer: Apache\r\nConnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n127\r\n<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<HTML><HEAD>\n<TITLE>400 Bad Request</TITLE>\n</HEAD><BODY>\n<H1>Bad Request</H1>\nYour browser sent a request that this server could not understand.<P>\nclient sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /<P>\n</BODY></HTML>\n\r\n0\r\n\r\n"}
4> gen_tcp:close(Sock).
ok
5>

这次我们把接收缓冲区里的东西拉干净了。

再看下tcpdump:

...
17:36:07.236627 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: S 3086473299:3086473299(0) win 5840 <mss 1460,sackOK,timestamp 2608642228 0,nop,wscale 7>
17:36:07.274661 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: S 738551248:738551248(0) ack 3086473300 win 8190 <mss 1436>
17:36:07.274685 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: . ack 1 win 5840
17:36:10.295795 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: P 1:19(18) ack 1 win 5840
17:36:10.334280 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: . ack 19 win 5840
17:36:10.334547 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: P 1:179(178) ack 19 win 5840
17:36:10.334554 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: . ack 179 win 6432
17:36:10.334563 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: P 179:486(307) ack 19 win 5840
17:36:10.334566 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: . ack 486 win 7504
17:36:19.671374 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: F 19:19(0) ack 486 win 7504
17:36:19.709619 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: . ack 20 win 5840
17:36:19.709643 IP 123.125.114.144.http > my031089.sqa.cm4.tbsite.net.9405: F 486:486(0) ack 20 win 5840
17:36:19.709652 IP my031089.sqa.cm4.tbsite.net.9405 > 123.125.114.144.http: . ack 487 win 7504
...

这次是发fin包了。

多隆同学再进一步,找出来之前squid client代码中不能理解的一句话:
client_side.c

...
 /* prevent those nasty RST packets */
    {
        char buf[SQUID_TCP_SO_RCVBUF];
        while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0);
    }
...

总算明白了这句话的意思了!

小结:认真学习协议栈太重要了。

玩得开心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

相关 [tcp 链接 fin] 推荐:

TCP链接主动关闭不发fin包奇怪行为分析

- T.C - 淘宝核心系统团队博客
多隆同学在做网络框架的时候,发现一条tcp链接在close的时候,对端会收到econnrest,而不是正常的fin包. 通过抓包发现close系统调用的时候,我端发出rst报文, 而不是正常的fin. 这个问题比较有意思,我们来演示下:. 我们往baidu的首页发了个http请求,百度会给我们回应报文的,我们send完立即调用close..

TCP的几个状态 (SYN, FIN, ACK, PSH, RST, URG)

- - 操作系统 - ITeye博客
在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 其中,对于我们日常的分析有用的就是前面的五个字段. PSH表示有 DATA数据传输,. 其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,.

TCP短链接调优

- - 操作系统 - ITeye博客
最近在做一个项目,用到HttpClient查询数据,由于服务端强制做成了短链接(大家都知道http1.1默认是带有keepalive机制),导致了请求方TCP状态很多都是TIME_WAITZ状态,端口被全部占用,请求失败. net.ipv4.tcp_tw_reuse = 1 表示开启重用. 允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;.

tcp/ip调优

- Lucseeker - 在路上
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;.

浅谈TCP优化

- - 火丁笔记
很多人常常对 TCP优化有一种雾里看花的感觉,实际上只要理解了TCP的运行方式就能掀开它的神秘面纱. Ilya Grigorik 在「 High Performance Browser Networking」中做了很多细致的描述,让人读起来醍醐灌顶,我大概总结了一下,以期更加通俗易懂. 传输数据的时候,如果发送方传输的数据量超过了接收方的处理能力,那么接收方会出现丢包.

TCP报文结构

- - 互联网 - ITeye博客
一、TCP报文结构如下:.  固定首部长度为20字节,可变部分0~40字节,各字段解释:. source port number:源端口,16bits,范围0~65525. target port number:目的端口,16bits,范围同上. sequence number:数据序号,32bits,TCP 连接中传送的数据流中的每一个字节都编上一个序号.

TCP 状态变化

- - 互联网 - ITeye博客
关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况. 前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接. 将关闭部分的状态转移摘出来,就得到了下图:. 通过图上,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢.

TCP/IP分享——链路层

- Goingmm - 弯曲评论
在张国荣自尽8周年纪念日,也就是愚人节的前几十分钟,终于把第二章弄完了. 首席似乎不是特别有空,我就斗胆在这里自己发了,从前面2期的反响来看,相当热烈,我也是摆出一副要杀要剐,悉听尊便的架势,这可能是受最近流行霸气外露的影响,批评几句又伤不了皮毛,也影响不了我的工作和正常生活,只要给大家带来快乐,我就很开心,似乎历史上很多想法都是在争吵中诞生的.

TFO(tcp fast open)简介

- chenqj - pagefault
原创文章,转载请注明: 转载自pagefault. 本文链接地址: TFO(tcp fast open)简介. 这个是google的几个人提交的一个rfc,是对tcp的一个增强,简而言之就是在3次握手的时候也用来交换数据. 这个东西google内部已经在使用了,不过内核的相关patch还没有开源出来,chrome也支持这个了(client的内核必须支持).