TCP三次握手
-
参数特性
大写的SYN ACK都是标志位。标志位唯一确定报文行为,取值范围: 0 或 1
SYN:synchronize , 标识该报文请求同步
ACK:acknowledgement, 标识该报文确认收到请求小写的seq是序号,ack是确认序号。序号位可以是用于通信的任意整数,取值范围:[0,232-1]
seq:sequence number
ack:acknowledgement number
因为一台主机可以发出N个TCP握手请求,所以要用seq和ack来区分不同端口的TCP请求。
宏观理解TCP三次握手:两次同步,一次激活
假设:服务端,客户端都用80端口。
-
客户端请求同步,SYN置1,根据本地TCP端口占用情况计算出seq。
【SYN = 1 | seq = x告诉服务器:“只是第一次握手请求,请求同步,x是这条通话的标识” -
服务端响应同步请求,SYN置1,作出响应,ACK置1。根据本地TCP端口情况计算出seq。
【SYN = 1, ACK = 1 | seq = y, ack = x+1】 由于响应要告诉请求方,本次响应的是哪条tcp请求。ack就发挥了作用,ack置为 x + 1。
告诉客户端:“你发送的x序列的第一次同步请求已通过,这边可以同步,请尽快激活,y是这条通话的标识。” -
客户端响应服务端的激活请求,ACK置1。由于服务端可能发出不同端口的TCP激活邀请,所以seq = x+1有两个语义:
3.1 只有ACK置1,证明之前已有80端口发送过第一次同步请求,不再本地计算出seq。取之前的x再加1标识这是80端口的第二次请求
3.2 只有ACK置1,证明这是个激活连接,如何告诉服务端需要激活哪次连接。自然就可以用激活信息中携带的响应序列【ACK =1 | seq = x+1, ack = y+1】告诉服务端:“我方序列为x的同步已完成,已激活你方序列为y的激活邀请,可以保持全双工通信”
微观理解三次握手中的seq,ack
-
seq
一次 TCP连接,客户端、服务器 都唯一占用 一个TCP 端口。
一个 TCP端口,不同 时间段 可以属于不同的TCP连接。
seq序列是一个循化队列。当客户端发起第一次握手,本地会(伪随机)生成一个seq,保证seq在一个生命周期内不会重复。服务器响应同理,也是占用端口资源。客户端的seq = x与服务器的seq = y是无关的,侧面反映出不同计算机的资源情况不同。客户端seq = x+1是确保自身握手消耗的资源连续性,这样下个时间段的TCP连接在循环队列内取到的资源也尽可能保证是连续的。 -
ack
不论客服端、服务器,都用seq标识自身资源。服务器如何告诉客户端,响应特定资源下的请求?答案是使用ack若客户端有seq = x 的报文①
服务器响应报文②,只需要响应 ack = x+1;
此后客户端不再发送seq = x 的报文
对应的seq = x的资源已经使用,但是在循环队列里,x不能在一个生命周期里再次使用。仅从三次握手过程,再进一步深入,TCP对ack是做了优化
若主机A发送报文 seq = x , seq = x + 1, seq = x + 2。使用这三个资源来串接成一个完整信息给主机B
主机B解析出完整信息后,只需要响应ack = x + 3,即可告诉主机A前面的资源都已成功被接收。推到得出
【SYN =1, ACK=1】中服务端的ack = 客户端seq + 1。
【ACK =1】中客户端的ack = 服务端seq +1
任意主机需要响应时, ack = 最后一次的seq + 1即可。seq和ack的微观结论也可用在四次挥手中
TCP四次挥手
- 参数特性
大体与三次握手相同,只是将SYN换成了FIN
宏观理解TCP四次挥手
- 为什么是四次
三次握手用的是“两次同步,一次激活”,其实挥手的时候也遵循这个原则。一端若主动关闭连接,另一端则确认是被动关闭。可以确定的是,主动方将不再发送数据,并发出“干完这一批就别干了!”的命令,被动方接到指令后有可能还有数据没发送完毕。则需要在CLOSE-WAIT状态下继续向主动方传输剩下的数据。所以第二、第三次挥手可以视作TCP三次握手的第二次同步。只是这个同步有处理残留数据的职责。 - 为什么需要TIME_WAIT
- 确保服务器有足够的时间能收到ACK报文
若服务器没有收到ACK报文,则会重发FIN报文,客户端也有足够时间重发ACK报文。是一种容错机制。 - 避免丢包(可能)
由于有些路由器会缓存IP数据包,虽然TCP已经断开,但是路由器缓存没有更新。如果没有这个时间,会丢失一部分数据包。
微观理解TCP四次挥手的seq和ack
- 为什么第二第三次的seq分别是v和w
夹在第二次第三次挥手中间的是残余数据的处理, w-v是残余数据处理时消耗掉的序列。客户端无需知道服务器返回了多少seq,只要拿到【FIN=1, ACK=1】的报文,就当做残留数据已处理完毕。所以第四次挥手ack = w + 1;