TCP三次握手和四次挥手深入实践

TCP连接状态

图1是TCP三次握手、数据传输、四次挥手三个阶段的状态转移图,状态说明如下:

  • LISTEN:侦听来自客户端的TCP端口的连接请求
  • SYN-SENT:再发送连接请求后等待匹配的连接请求(如果有大量这样的状态包,检查是否中招了)
  • SYN-RCVD:再收到和发送一个连接请求后等待对方对连接请求的确认(如有大量此状态,估计被flood攻击了)
  • ESTABLISHED:代表一个打开的连接
  • FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认
  • FIN-WAIT-2:从远程TCP等待连接中断请求
  • CLOSE-WAIT:等待从本地用户发来的连接中断请求
  • LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认(不是什么好东西,此项出现,检查是否被攻击)
  • TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认
  • CLOSED:没有任何连接状态,连接结束

本文通过实践例子模拟每个阶段的状态变化。

图1 TCP连接状态转移图

本文用tcpdump抓包分析TCP连接的交互过程,其中tcpdump Flags含义如下:

  • S=SYN 发起连接标志
  • P=PUSH 传送数据标志
  • F=FIN 关闭连接标志
  • R=RESET 异常关闭连接,链接重置
  • . 表示没有任何标志,表示返回ack

为什么要三次握手?

  • 如果只有一次握手,Client不能确定与Server的单向连接,更加不能确定Server与Client的单向连接;
  • 如果只有两次握手,Client确定与Server的单向连接,但是Server不能确定与Client的单向连接;
  • 只有三次握手,Client与Server才能相互确认双向连接,实现双工数据传输。
图2 TCP三次握手

为什么要四次挥手?

“三次握手”的第二次握手发送SYN+ACK回应第一次握手的SYN,但是“四次挥手”的第二次挥手只能发送ACK回应第一次挥手的FIN,因为此时Server可能还有数据传输给Client,所以Server传输数据完成后才能发起第三次挥手发送FIN给Client,等待Client的第四次挥手ACK。

图3 TCP四次挥手

下面通过例子模拟TCP连接过程的状态转移。

情况1:Client启动服务,Server不启动服务

Server(127.0.0.1:2017)未启动服务,如图4抓包所示,Client(127.0.0.1:32906)发送SYN(localhost.32906 > localhost.2017: Flags [S])启动TCP连接,Server返回localhost.2017 > localhost.32906: Flags [R.],R=RESET表示异常关闭连接,连接重置。

图4 Client启动服务,Server不启动服务(tcpdump)
情况2:Server启动服务,Client不启动服务

如图5所示,Server(127.0.0.1:2017)监听2017端口,TCP连接状态是LISTEN,等待Client发起TCP连接,如图6所示,tcpdump抓包没有连接数据。

图5 Server启动服务,Client不启动服务(netstat))
图6 Server启动服务,Client不启动服务(tcpdump)
情况3:Client连接Server

如图7所示,Server(127.0.0.1:2017)启动2017端口监听连接,Client(127.0.0.1:55702)启动55702端口访问2017端口的Server,由图7、图1和图2比对,两端的双向连接已经建立,TCP状态转移到ESTABLISHED。正常情况下,SYN-SENT和SYN-RCVD转移非常快,netstat难以捕获,捕获场景请查看下面异常情况1和异常情况2。
如图8抓包所示,三行数据对应图2的三次握手,说明双向连接已经建立。

图7 Client连接Server(netstat)
图8 Client连接Server(tcpdump)
情况4:Client传输数据

从图1和图9看,数据传输过程中Server(127.0.0.1:2017)和Client(127.0.0.1:55866)的TCP连接状态均为ESTABLISHED。

图9 Client数据传输(netstat)

从图10红色方框看,Client(localhost:55866)向Server(localhost:2017)发送数据(localhost.55866 > localhost.2017: Flags [P.]),Server返回ack(localhost.2017 > localhost.55866: Flags [.])确认。

图10 Client数据传输抓包(tcpdump)
情况5:Client断开连接,Server保持连接

Client(localhost.41416)主动断掉TCP连接,进入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT状态,分别顺序对应图11红色方框的三行记录,对比图3,第一行表示第一次挥手,第二行表示第二/三次挥手,第三行表示第四次挥手。

图11 Client断开连接,Server保持服务(tcpdump)

对比12和图3,Client(127.0.0.1:41416)主动断掉TCP连接,进入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT状态,等待2MSL,如果2MSL内Server(127.0.0.1:2017)没有发送FIN,意味着Server已经接收到Client的FIN ACK,1分钟(/proc/sys/net/ipv4/tcp_fin_timeout:60)超时后Client TCP状态转移为CLOSED,符合TCP四次挥手逻辑。

图12 Client断开连接,Server保持服务(netstat)
情况6:Server断开连接,Client保持连接

Server(localhost:2017)主动断掉TCP连接,根据图13倒数第二行,对比图3(Server/Client反着对比),Server发送FIN后TCP状态由ESTABLISHED转移到FIN-WAIT-1,根据图13倒数第一行,Server接收到Client(localhost:40277)发送的FIN ACK,由FIN-WAIT-1转移到FIN-WAIT-2状态,如图14所示,超时后转移到CLOSED。
如图14所示,Client(127.0.0.1:40277)回应Server FIN ACK,但是Client未断开连接,TCP状态由ESTABLISHED转移为CLOSE_WAIT,不会由CLOSE_WAIT转移为LAST-ACK。
如图15所示,Client(localhost:40277)关闭连接,发送FIN,Client由CLOSE_WAIT -> LAST-ACK -> CLOSED。Server(localhost:2017)已经关闭,故回复localhost.2017 > localhost.40277: Flags [R],R=RESET表示异常关闭连接,连接重置。

图13 Server断开连接,Client保持连接(tcpdump)
图14 Server断开连接,Client保持连接(netstat)
图15 Client关闭连接(tcpdump)

正常情况下,SYN-SENT、SYN-RCVD、LAST-ACK这些状态转移非常快,netstat很难查看到,如果netstat出现这些状态,有可能受到攻击,下面将模拟这些场景。

异常情况1:模拟出现SYN-SENT

从图2看到,正常情况,三次握手Client的TCP状态很快转移到ESTABLISHED,不会长时间停留在SYN_SENT。但是如果Server不返回SYN ACK,Client通过netstat可以查看到SYN_SENT。
1.设置防火墙iptables丢弃发送给Server(127.0.0.1:2017)的数据包,这样Server不会响应Client发送的SYN而返回SYN ACK。如图16,把红色打叉的传输过程掐断,防火墙丢弃Client发送给Server的SYN。

iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP
图16 设置防火墙丢弃Client发送的SYN

2.Client(localhost:35996)发送SYN给Server(localhost:2017),如图17所示,从localhost.35996 > localhost.2017: Flags [S]行记录看,Client TCP连接状态转移到SYN_SENT,防火墙丢弃发送给Server的SYN,Server端也就不会发送SYN ACK给Client,结合图16和图18,Client TCP连接状态不会由SYN_SENT转移到ESTABLISHED,Server TCP连接状态不会由LISTEN转移到SYN_RCVD。
如图17红色方框所示,Client端以2的倍数递增的间隔重试6次,重试时间间隔:1s、2s、4s、8s、16s、32s。如图18、19所示,Client TCP连接状态保持在SYN_SENT,超时后转移到CLOSED,等待时长对应图17的重试时间。

图17 模拟出现SYN-SENT(tcpdump)
图18 Client端TCP连接状态SYN_SENT等待时长1
图19 Client端TCP连接状态SYN_SENT等待时长2

3.客户端握手超时报错信息:Connection timed out

Exception in thread "main" java.net.ConnectException: Connection timed out (Connection timed out)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at java.net.Socket.connect(Socket.java:538)
    at java.net.Socket.<init>(Socket.java:434)
    at java.net.Socket.<init>(Socket.java:211)
    at io.socket.Client1.main(Client1.java:17)
异常情况2:模拟出现SYN-RCVD

从图2看到,正常情况,Server的TCP连接状态很快转移到ESTABLISHED,不会长时间停留在SYN_RCVD。但是如果Client不返回SYN ACK,Server通过netstat可以查看到SYN_RCVD。
1.设置防火墙丢弃Server(127.0.0.1:2017)发送出去的数据包,这样Client不会响应Server发送的SYN而返回SYN ACK。如图20,把红色打叉的传输过程掐断,防火墙丢弃Server发送的SYN&ACK。

iptables -I INPUT -s 127.0.0.1 -p tcp --sport 2017 -j DROP
图20 设置防火墙丢弃Server发送的SYN&ACK

2.Client(localhost:36436)发送SYN给Server(localhost:2017),Server发送SYN ACK给Client,但是被防火墙丢弃Server发送Client端的SYN&ACK,Client也就不会发送SYN ACK给Server,结合图20和图22,Client TCP连接状态转移为SYN_SENT,但是不会转移到ESTABLISHED,Server TCP连接状态由LISTEN转移到SYN_RCVD,但是不会转移到ESTABLISHED。
如图21红色方框所示,Client收不到Server 的SYN ACK,从localhost.36436 > localhost.2017: Flags [S.]的行记录看,Client以2的倍数递增的间隔重试6次,重试时间间隔:1s、2s、4s、8s、16s、32s。
Server收不到Client发送的SYN ACK,从localhost.2017 > localhost.36436: Flags [S.]的行记录看,Server端以2的倍数递增的间隔重试5次,重试时间间隔:1s、2s、4s、8s、16s。
如图22所示,Client TCP连接状态保持在SYN_SENT,超时后转移到CLOSED,等待时长对应图21的重试时间。Server TCP连接状态保持在SYN_RCVD,超时后转移到CLOSED,等待时长对应图21的重试时间。

图21 模拟出现SYN-RCVD(tcpdump)
图22 TCP连接状态SYN_SENT/SYN_RCVD等待时长
异常情况3:模拟出现FIN_WAIT1

从图3看到,正常情况,四次握手Client断开连接后,TCP连接状态很快转移到FIN_WAIT2,不会长时间停留在FIN_WAIT1。但是如果Server不返回FIN ACK,Client通过netstat可以查看到FIN_WAIT1。
1.Client在断开连接之前,设置防火墙丢弃Client发送给Server(127.0.0.1:2017)的数据包,这样Server不会响应Client发送的FIN而返回FIN ACK。如图23,把红色打叉的传输过程掐断,防火墙丢弃Client发送的FIN。

 iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP
图23 设置防火墙丢弃Client发送的FIN

2.设置防火墙丢弃Client(127.0.0.1:40604)发送出去的包,Client主动断开连接,此时Server保持连接,如图24所示, 从localhost.40604 -> localhost.2017 Flags [F.]的行记录,Client发送FIN给Server,但是被防火墙丢弃,结合图23和图25,Client的TCP状态转移过程:ESTABLISHED -> FIN_WAIT1,Client收不到Server发送的FIN ACK, TCP状态不能由FIN_WAIT1转移到FIN_WAIT2;Server TCP状态为ESTABLISHED,Server接收不到Client的FIN而没有发送FIN ACK, TCP状态不能由ESTABLISHED转移到CLOSE_WAIT。
如图24红色方框所示,Client收不到Server发送的FIN ACK,从localhost.40604 -> localhost.2017 Flags [F.]行记录看,Client重试6次,重试时间间隔:1s、1s、2s、3s、6s、14s、26s。
如图25所示,Client TCP连接状态保持在FIN_WAIT1,超时后转移到CLOSED,等待时长对应图24的重试时间。

图24 模拟出现FIN_WAIT1(tcpdump)
图25 TCP连接状态FIN_WAIT1等待时长
异常情况4:模拟出现LAST-ACK

从图3看,Server主动断开连接,实际上此时Server变成客户端,Client和Server的TCP状态反着查看TCP四次挥手,Client发送FIN&ACK后CLOSE-WAIT -> LAST-ACK,接收到Server的FIN ACK后LAST-ACK -> CLOSED。
1.Server(127.0.0.1:2017)主动断开连接后,Client(127.0.0.1:40541)主动断开连接前,设置防火墙丢弃Client发送出去的包,这样Server不能接收到Client发送的FIN,Client(127.0.0.1:40541)TCP状态由CLOSE-WAIT过渡到LAST-ACK,超时由LAST-ACK转移为CLOSED。如图26,把红色打叉的传输过程掐断,防火墙丢弃Client FIN。

iptables -I INPUT -s 127.0.0.1 -p tcp --sport 40541 -j DROP
图26 设置防火墙丢弃Client FIN

2.Server(127.0.0.1:2017)主动断开连接,此时Client(127.0.0.1:40541)保持连接,如图27所示, localhost.2017 > localhost.40541 Flags [F.],Server发送FIN给Client,结合图26和图28,Server的TCP状态转移过程:ESTABLISHED -> FIN_WAIT1 -> FIN_WAIT2,Client的TCP状态由ESTABLISHED转移为CLOSE_WAIT。
如图27红色方框所示,因为Client(localhost:40541)发给Server的FIN被防火墙丢弃,所以Client收不到Server发送的FIN ACK,从localhost.40541 > localhost.2017 Flags [F.]行记录看,Client重试7次,重试时间间隔:1s、1s、1s、4s、6s、13s、26s。
设置防火墙丢弃Client(127.0.0.1:40541)发送出去的包,紧接着Client主动断开连接,结合图26和图28,Client的TCP状态由CLOSE_WAIT转移为LAST_ACK,因为Server(127.0.0.1:2017)接收不到Client发送的FIN(已被防火墙丢弃),也就不能给Client发送FIN ACK,LAST_ACK不能直接转移为CLOSED,超时后LAST_ACK -> CLOSED,超时时长对应图27的重试时长。

图27 模拟出现LAST-ACK(tcpdump)
图28 TCP连接状态LAST-ACK等待时长(netstat)

参考文档

tcp 三次握手和四次断连深入分析:连接状态和socket API的关系

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容