本文从三次握手角度四次挥手来带你快速理解TCP
TCP报文段
在将三次握手之前,先来看看每次握手都发送了什么东西
这里不对报文段作详细解释,只讲解下三次握手四次挥手有关的字段
- 序号
Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
- 确认号
Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
- ACK:确认序号有效。占1位,值0/1。
- SYN:发起一个新连接。占1位,值0/1。
- FIN:释放一个连接。占1位,值0/1。
四次挥手时用到,表明断开连接
三次握手
所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。
最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。
注意,三次握手握的是通信双方数据原点的序列号。
再来看下什么是序号和确认号
序号,确认号
在开启TCP会话时,客户端和服务端都会给自己的数据编号,这就是序号/序列号,初始序列号是随机的,0~2^32之间。
此时客户端待发送的数据首序号为1001,服雾端待发送数据首序号为2001。注:客户端和服务端序号系统是独立的
- 假设客户端发送的报文中,序号:1001,携带的数据为200
- 服务端成功接受到这200数据后,期望客户端下一次发送的数据是从1001+200开始的,发送给客户端的报文中序号为2001,确认号为1001+200,同时服务器也发送100数据
- 客户端收到服务端的确认号1201,验证无误,同时收到服务端100的数据,然后再向服务端发送300的数据,那么这300的数据序列号肯定是1201-1500,这时客户端发送给服务端的报文应为
序列号:1201(要发送的数据首序号,同时也是服务器刚刚发来的确认号)
确认号:2200(从服务器发来的报文中得知服务器发送的首序列号为2001,又因为服务端还携带了200的数据,所以确认号应为2201,告诉服务器下次发给我的数据应当从2201开始发送,但是发送多少我不管,服务端可以从2201判断出客户端成功收到了那200的数据)
客户端发送给服务端那300的数据
...
简而言之,序列号是自己的序列号,确认号
是对方发来的包中的(序列号+数据长度),因为确认号是期望收到对方下一个报文的第一个数据字节的序号。
第一次握手:客户端->服务端
当某个主机开启一个TCP会话时,他的初始序列号是随机的,这里假定客户端初始序号为1000,服务端初始序号为2000
TCP会话的每一端都包含一个32位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收
所以在TCP报文段中:
- 序号:X,如1000
- ACK: 0 (不是应答报文)
- SYN: 1 (表明发起一个新连接)
因为这是第一个包,此时通话还未开始,没有通话的另一端需要确认,所以这个包中的确认号没有意义
TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
第二次握手:服务端->客户端
TCP服务器收到请求报文后,如果同意连接,则发出确认报文。
报文段中
- 序列号: seq=y,假设2000
该初始序列号是随机的
- 确认号: ack=x+1,1001 = 1000 + 1 (客户端消耗的那1个序号)
- ACK=1
- SYN=1
同样,SYN=1,这个报文也不能携带数据,但是要消耗一个序号。
第三次握手:客户端->服务端
TCP客户进程收到确认后,还要向服务器给出确认
确认报文:
- 序号: seq=x+1 如1001 = 1000 + 1 (自身的序号在发送第一个包的时候被消耗了)
- 确认号:ack=y+1 如2001 = 2000 + 1 (服务端应答包消耗的那1个序号)
- SYN: 0
- ACK: 1
此时,TCP连接建立。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
四次挥手
TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
第一次:客户端->服务端
客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)
发送完毕后,客户端进入 FIN_WAIT_1 状态。
TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第二次:服务端->客户端
服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
第三次:服务端->客户端
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第四次:客户端->服务端
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
注意此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。服务器结束TCP连接的时间要比客户端早一些。