本文通过wrieshark抓取TCP包,来对TCP进行细致的解析,从实践中了解TCP各个过程。
首先介绍下TCP的6种标志位,wriehark会根据这些标志位,在抓取的包中进行标记,方便我们查看:
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)
1、三次握手建立连接:
如上图所示,第一次握手,客户端(端口号为44882)向服务器(端口号为5555)发送【SYN】,其中Seq=j=0。第二次握手,服务器回一个【SYN,ACK】,其中Seq=k=0,Ack=j+1=1。第三次握手,客户端向服务器发一个【ACK】,其中Seq=j+1=1(上一个包序号是j,当前这个包为j+1),Ack=k+1=1。
客户端通过connect建立连接进行三次握手。服务器端三次握手之后调用accept函数,返回文件描述符。
2、数据传输:
如上图所示,数据传输时,发送方(端口号为44882)发送【PSH,ACK】:序号为Seq=j=1,Ack=i=1。接收方接收到数据后,会回一个【ACK】:其中ACK中的Seq=i=1,是收到的【PSH,ACK】中的Ack值,表示是Ack=i=1的确认包;Ack=m=3表示当前收到3号之前序号的包。所以接下来,发送端会继续发送【PSH,ACK】:Seq=m=3,也就是收到【ACK】中的Ack值。可以通过Seq和Ack来判断数据是否丢失,是否需要重传。
3、断开连接
(1)四次握手断开连接
如上图所示,第一次握手,主动断开连接一方(端口号为44882)发送【FIN,ACK】;被动断开连接一方(端口号为5555)接收到【FIN,ACK】后,回一个【ACK】,接着发一个【FIN,ACK】到主动断开连接一方;主动断开连接一方接收到【FIN,ACK】后也会回一个【ACK】,这样四次握手断开连接就结束了。
是不是感觉四次握手很简单,如果你这么想的话你就错了。接下来深入去挖掘断开连接的过程
如上图(网上借鉴过来的,博主写的很好,可以去借鉴一下https://blog.csdn.net/kkgbn/article/details/77859881)所示,主动断开一方close()关闭句柄,发送【FIN,ACK】后,该方这条通路的句柄会处于FIN_WAIT_1状态(可以通过netstat -at命令查看);被动断开一方回复完【ACK】时,该方这条通路的句柄会处于CLOSE_WAIT状态;主动断开一方收到【ACK】后,该句柄会处于FIN_WAIT_2状态;当被动断开一方close()关闭句柄时,会发送【FIN,ACK】,该句柄处于LAST_ACK状态(这个没有验证);主动断开一方收到【FIN,ACK】之后,回一个【ACK】,该句柄会处于TIME_WAIT状态,2ms后回彻底关闭;被断开一方收到【ACK】会彻底断开。
(2)三次握手断开连接
上面是四次握手断开连接,还存在三次握手断开连接的情况(两方同时断开连接),如下图所示
当被断开一方阻塞在recv函数时接收到【FIN,ACK】,此时recv会立刻返回0,被断开连接一方不回复【ACK】而是直接发送【FIN,ACK】。
(3)断开过程的一些细节
被断开连接一方处于CLOSE_WAIT状态和LAST_ACK状态之间,是可以继续发送数据的,但是此时主动断开连接一方处于FIN_WAIT_2,它接收到数据时会发送一个【RST,ACK】,当被断开连接一方再发送数据时,程序会异常退出。
当被断开连接一方一直没有close关闭连接或者程序没有退出,句柄会一直停留在CLOSE_WAIT状态,造成端口不可用;关闭句柄,就会发出【FIN,ACK】,就不能够再发送数据。