TCP,全称 Transmission Control Protocal。从名字可以知道这是一个用于 控制传输 的位于传输层的协议。
TCP 位于 TCP/IP 和 OSI 模型的传输层。我们最常使用的 HTTP 协议,底层通常使用的就是 TCP 协议。
如果要在客户端和服务端创建 TCP 连接,我们需要在开始的时候发送三个请求确认双方的通信能力正常,这三次连接就被称为 TCP 的三次握手。
建立 TCP 连接一段时间后,如果要断开 TCP 连接,就会进行 TCP 四次挥手过程完成断开操作。
目录
一、环境准备
1.1 本机环境
1.2 安装Wireshark
二、TCP标志位的6种标示
三、三次握手
3.1 发一次请求
3.2 三次握手分析(结合图3-3)
3.2.1 No764第一次握手
3.2.2 No765第二次握手
3.2.3 No766第三次握手
3.3 为什么要三次握手?
四、四次挥手
4.1 四次握手分析(结合图4)
4.1.1 No772 第一次挥手
4.1.2 No775 第二次挥手
4.1.3 No776 第三次挥手
4.1.4 No777 第四次挥手
4.2 为什么要四次挥手?
五、状态迁移详解
六、TIME-WAIT状态详解
一、环境准备
1.1 本机环境
- 操作系统: MacOs Sonoma 14.0
- 网络分析工具:Wireshark
1.2 安装Wireshark
- windows下载地址:Wireshark
- Mac下载地址:Wireshark · Go Deep
- 本例安装包4.0.8下载资源地址:https://download.csdn.net/download/imwucx/88399757
二、TCP标志位的6种标示
- SYN(synchronous建立联机)
- ACK(acknowledgement 确认)
- PSH(push传送)
- FIN(finish结束)
- RST(reset重置)
- URG(urgent紧急)
- Sequence number(顺序号码)
- Acknowledge number(确认号码)
三、三次握手
打开Wireshark,界面如下:
图 3
- 上部分窗口:为数据包概要信息窗口
- 左下角窗口:为数据包解析窗口
- Frame:表示该帧数据的整体信息
- Ethernet:数据链路层
- Internet:网络层
- Transmission Control Protocol:传输控制协议
- Hypertext transfer protocol:超文本传输协议
- 右下角窗口:为数据包数据窗口
3.1 发一次请求
拼通度娘ip为14.119.104.189,如下:
图 3-1
向度娘发一次请求curl www.baidu.com如下:
图 3-2
查看Wireshark,找到14.119.104.189网络分析包如下:
图 3-3
- No:发生交易序号
- Time:发生交易时间
- Source:发起交易的机器
- Destination:发起交易目的的
- Protocol:协议
- Length:请求包长度
- Info:请求信息
3.2 三次握手分析(结合图3-3)
通俗的理解:(客户端与服务端通话)
- 客户端:我请求与你连接通话
- 服务端:你确定么?
- 客户端:我确定
3.2.1 No764第一次握手
第764次的交易内容info如下:(图3-3 No 764)
49181 → 80 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=64 TSval=3874561489 TSecr=0 SACK_PERM
- Client 端向服务器发送连接请求,并发送SYN信号包到服务器,Seq为0。
- 其中,客户端与服务连接端口:49181为本机端口,80为服务端端口。
3.2.2 No765第二次握手
第765次的交易内容info如下:(图3-3 No 765)
80 → 49181 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1452 WS=32 SACK_PERM
- Server 收到请求后,必须确认客户的数据包。同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RCVD状态。(其中确认报文段中,这是一个TCP连接响应数据报文,并含服务端的初始序号Seq,以及服务器对客户端初始序号的确认号Ack(服务器)=Seq(客户端)+1= 0 + 1)。
- 其中,客户端与服务连接端口:49181为本机端口,80为服务端端口。
3.2.3 No766第三次握手
第766次的交易内容info如下:(图3-3 No 766)
49181 → 80 [ACK] Seq=1 Ack=1 Win=262144 Len=0
- Client 收到服务器的SYN+ACK包,检查Ack是否为Seq(第一次客户端Seq值)+1,即是否为1.确认后(如果正确),客户端向服务器发送ACK包(Ack(客户端)=Seq(服务器)+1= 0 + 1 = 1,标记Seq序为第一次握手Seq + 1 = 0 + 1 = 1)。
Server检查Ack是否为(服务端发送的seq值)0 + 1,ACK是否为1,如果正确则连接建立成功,Client端和Server端进入ESTABLISHED状态,完成三次握手,随后Client端与Server端之间可以开始传输数据了
。
- 其中,客户端与服务连接端口:49181为本机端口,80为服务端端口
3.3 为什么要三次握手?
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
- 由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起了SYN=1创建连接的请求(第一次握手)。如果服务器端就直接创建了这个连接并返回包含SYN、ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直没有接收到服务器返回的数据包,那么客户端一直发出请求,从而导致增加服务端的开销。
- 客户端可能设置了一个超时时间,时间到了就关闭了连接创建的请求。再重新发出创建连接的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器端客户端收的到服务器端传输的数据的话,服务器端是不知道客户端有没有接收到服务器端返回的信息的。
第一次客户端发送SYN时 什么也确认不了。
第二次服务端发送SYN+ACK 可以确认客户端发送功能正常。
第三次客户端收到服务端发送的YSN+ACK 可以确认服务端发送接收功能正常
最后客户端发送ACK 服务端接收到后 可以确认服务发送功能正常。
四、四次挥手
打开Wireshark,四次挥手 No772、No775、No776和No777
图 4
4.1 四次握手分析(结合图4)
通俗的理解:(客户端与服务端通话)
- 客户端:我说完了
- 服务端:我知道了,我还没说完,等我说完
- 服务端:好了,我也说完了
- 客户端:好嘞,挂了吧
4.1.1 No772 第一次挥手
第772次的交易内容info如下:(图4 No 772)
49181 → 80 [FIN, ACK] Seq=77 Ack=2782 Win=262144 Len=0
- 首先,客户端发送一个FIN进入FIN_WAIT1状态,这个FIN用来关闭客户端到服务器的数据传送,然后等待服务器的确认。Seq=j=77,Ack=k=2782。
- 我(Client端)没有数据要发给你(Server端)了",但是如果你(Server端)还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK告诉我你收到我消息了。
4.1.2 No775 第二次挥手
第775次的交易内容info如下:(图4 No 775)
80 → 49181 [ACK] Seq=2782 Ack=78 Win=29056 Len=0
- 服务器收到这个FIN进入CLOSE_WAIT状态,然后它给客户端发送一个ACK,确认ack为收到的序号加一,即Ack=j+1=77+1=78,Seq=k=2782。
- 告诉Client端,你的请求我收到了,但是我(Server端)还没准备好,请继续你等我的消息。
- 客户端收到ACK应答后进入FIN_WAIT2状态
4.1.3 No776 第三次挥手
第776次的交易内容info如下:(图4 No 776)
80 → 49181 [FIN, ACK] Seq=2782 Ack=78 Win=29056 Len=0
- 服务端关闭服务器到客户端的连接,发送一个FIN给客户端。服务端进入LAST_ACK状态。Seq=y=k=2782,Ack=x=j+1=77+1=78。
4.1.4 No777 第四次挥手
第777次的交易内容info如下:(图4 No 777)
49181 → 80 [ACK] Seq=78 Ack=2783 Win=262144 Len=0
- 客户端收到FIN后,进入TIME_WAIT状态 并发回一个ACK报文确认,确认ack为收到的序号加一,即Ack=y+1=2782+1=2783,Seq=j+1=77+1=78。
- 服务端收到客户端回复的ACK后立即关闭,服务端进入CLOASED状态。而客户端要等待2MSL后关闭 进入CLOASED状态。
- Client端收到FIN报文后,"就知道可以关闭连接了,所以发送ACK。但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后没有立即,而是进入TIME_WAIT状态,如果Server端没有收到ACK那么自己还可以重传。Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
4.2 为什么要四次挥手?
见3.3,为什么多了1次呢。因为挥手时有数据正在传输,有一个确认数据传输完的过程。
五、状态迁移详解
客户端状态迁移:CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务端状态迁移:CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
- LISTEN:侦听来自运方TCP端口的连接请求。表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接。
- SYN-SENT:在发送连接请求后等待匹配的连接请求。这个状态与SYN_RCVD状态相呼应,它是TCP连接客户端的状态,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随机进入到SYN_SENT状态,并等待服务端的SYN和ACK,该状态表示客户端的SYN已发送。
- SYN-RCVD:在收到和发送一个连接请求后等待对方连接请求的确认。表示服务器接收到了来自客户端请求连接的SYN报文。这个状态是在服务端的,但是它是一个中间状态,很短暂,平常我们用netstat或ss的时候,不太容易看到这种状态,但是遇到SYN flood之类的SYN攻击时,会出现大量的这种状态,即收不到三次握手最后一个客户端发来的ACK,所以一直是这个状态,不会转换到ESTABLISHED。
- ESTABLISHED:表示TCP连接已经成功建立,开始传输数据。
- FIN-WAIT-1:等待远程TCP的连接中断请求,或先前的连接中断请求的确认。这个状态在实际工作中很少能看到,当客户端想要主动关闭连接时,它会向服务端发送FIN报文,此时TCP状态就进入到FIN_WAIT_1的状态,而当服务端ACK,确认关闭后,则客户端进入到FIN_WAIT_2的状态,也就是只有在没有收到服务端ACK的情况下,FIN_WAIT_1状态才能看到,然后长时间收不到ACK,通常会在默认超时时间60s(由内核参数tcp_fin_timeout控制)后,直接进入CLOSED状态。
- FIN-WAIT-2:从远程TCP等待连接中断请求。这个状态相比较常见,也是需要注意的一个状态,FIN_WAIT_1在接收到服务端ACK之后就进入到FIN_WAIT_2的状态,然后等待服务端发送FIN,所以在收到对端FIN之前,TCP都会处于FIN_WAIT_2的状态,也就是,在主动断开的一端发现大量的FIN_WAIT_2状态时,需要注意,可能是网络不稳定或程序中忘记调用连接关闭,FIN_WAIT_2也有超时时间,也是由内核参数tcp_fin_timeout控制,当FIN_WAIT_2状态超时后,连接直接销毁。
- CLOSE-WAIT:等待用户发来的连接中断请求。表示正在等待关闭,该状态只在被动端出现,即当主动断开的一端调用close()后发送FIN报文给被动端,被动端必然会回应一个ACK(这是由TCP协议层决定的),这个时候,TCP连接状态就进入到CLOSE_WAIT。
- CLOSING:等待远程TCP对连接中断的确认。这个状态是一个比较特殊的状态,也比较少见,正常情况下不会出现,但是当双方同时都作为主动的一方,调用 close() 关闭连接的时候,两边都进入FIN_WAIT_1 的状态,此时期望收到的是ACK包,进入 FIN_WAIT_2 的状态,但是却先收到了对方的FIN包,这个时候,就会进入到 CLOSING 的状态,然后给对方一个ACK,接收到 ACK 后直接进入到 CLOSED 状态。
- LAST-ACK:等待原来发向远程TCP的连接中断请求的确认。当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK的状态,当收到对方的ACK之后,就进入到CLOSED状态了。
- TIME- WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认。该状态是最常见的状态,主动方在收到对方FIN后,就由FIN_WAIT_2状态进入到TIME_WAIT状态。
- CLOSED:没有任务连接状态。初始状态,表示TCP连接是”关闭着的”或”未打开的”。
六、TIME-WAIT状态详解
为什么Client端要先进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态?保证TCP协议的全双工连接能够可靠关闭,保证这次连接的重复数据段从网络中消失。
假设由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,Client端在等待2MSL时间后都没收到信息,说明Server端已经收到自己发送的ACK并且成功关闭了。
假设CLient端直接关闭了。
- 由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。
- 如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。
2MSL:Maximum Segment Lifetime 即数据在网络中保存的最大时间。
- 假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
参考:https://www.cnblogs.com/Jessy/p/3535612.html