1.要先了解TCP头部数据
16位(各自8位)端口号:表示该报文来自哪里(源端口),以及要传给哪个应用程序或者上层协议(目的端口)
进行TCP通信时,一般client是通过系统自动选择的临时端口号,而服务器一般都是使用知名的端口号或者直接指定端口号
32位序列号(sequence number):TCP连接中传输的字节流中的每个字节都按顺序编号
例如,一段报文的序号字段值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始
序列号在TCP通信中有两个作用:
1.在SYN报文中交换彼此的初始序列号
2.保证数据包按正确的顺序组装(ISN)
ISN(Intial sequence number)初始序列号,在三次握手的过程当中,双方会用过SYN报文来交换彼此的ISN
ISN并不是一个固定值,而是每4ms加1,溢出则回到0,这个算法使得猜测ISN变得很困难。
为什么要这样做?
如果ISN被攻击者预测到,要知道源IP和源端口都是很容易伪造的,当攻击者猜测ISN之后,直接伪造一个RST后,就可以强制连接关闭的,这是非常危险的。而动态的ISN大大提高了猜测ISN的难度
32位确认号(Acknowledgment number):是期望收到对方下一个报文的第一个数据字节的序号
例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
4位数据偏移(头部长度):表示TCP报文的数据距离TCP报文段的起始处多远
“数据偏移”的单位不是字节而是32bit字(4字节为计算单位),表示该TCP报头有多少个4字节(32个bit)
表示TCP头部有多少个32bit(比特),因为4位最大值是15,所以最多有15个32bit位,也就是60个字节是最大的TCP头部长度
6位保留字段:表示保留为今后使用,但目前应置为0
6位标记(flags)位:
URG 表示紧急指针是否有效
ACK 表示确认号(确认序号)是否有效
PSH 用来提示接收端应用程序立刻将数据从TCP缓冲区读走
RST 要求重新建立连接,我们把含有RST标识的报文称为复位报文段
SYN 请求建立连接,我们把含有SYN标识的报文称为同步报文
FIN 通知对端,本端即将关闭,我们把含有FIN标识的报文称为结束报文段
1.ACK是可能与SYN,FIN等同时使用的
比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应
如果只是单个的一个SYN=1,它表示的只是建立连接。
2.TCP的几次握手就是通过这样的ACK表现出来的。
3.SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。
4.RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。
5.一般出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接
6.出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接
7.PSH=1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH=1表示的是有真正的TCP数据包内容被传递。
16位窗口大小:通知接收方,发送本报文你需要有多大的空间来接受
是TCP流量控制的一个手段,这里说的窗口是指接收通知窗口,它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度
16位校验和:校验首部和数据这两部分
由发送端填充,接收端对TCP报文执行CRC算法以及TCP报文段在传输过程中是否损坏。注意这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障
16位紧急指针:用来标识哪部分数据是紧急数据
选项:长度可变,定义一些其他可选参数
如何标识唯一一个连接?
TCP连接的四元组:源IP、源端口、目标IP、目标端口
那TCP报文中这么没有源IP和目标IP?
因为在IP层就已经处理了IP,所以TCP只需要记录两者端口即可。
2.三次握手
恋爱模式
以谈恋爱为例,两个人能够在一起最重要的事情是首先确认各自爱和被爱的能力。
第一次:
男:我爱你
女法收到
由此证明男方拥有爱的能力
第二次
女:我收到了你的爱,我也爱你
男方收到
ok,现在的情况说明,女方拥有爱和被爱的能力
第三次
男:我收到了你的爱
女方收到
现在能够证明男方具备被爱的能力
由此完整的确认了双方爱和被爱的能力,两人开始了一段甜蜜的爱情故事
TCP握手,是要确定双方的两样能力:发送能力和接收能力
TCB传输控制块Transmission Control Block:存储每一个连接中的重要信息,如TCP连接表,到发送和接收缓存的指针,到重传队列的指针,当前的发送和接收序号。
1.服务器进程会先创建TCB(传输模块),服务端开始监听某个端口,时刻接收客户端传输过来的数据,进入listen(监听)状态
2.(第一次握手)客户端进程会先创建TCB(传输模块)然后主动发送连接请求报文,这是报文首部中的同部位SYN=1,同时初始化序列号seq=x,此时TCP客户端进程进入SYN-SENT(同步已发送状态)状态。
TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号
3.(第二次握手)服务端接收到请求报文后,如果同意连接,则发出确认报文(确认报文中应该ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号seq=y),TCP服务器进程进入了SYN-RCVD(同步收到)状态。
这个报文也不能携带数据,但是同样要消耗一个序列号。
4.(第三次握手)客户端进程收到确认后(对服务端返回的数据进行确认 --- 检查ack是否为x+1,ACK是否为1),
还要向服务器给出确认报文(确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1),TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。
TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号
5.(第三次握手)当服务器收到客户端的确认后(对客户端返回的报文进行确认------检查ack是否为y+1,ACK是否为1)
也进入SETABLISHED状(已建立连接)状态,此后双方就可以开始通信了。
为什么不是两次握手,而是三次握手?为什么客户端还要在发一次确认呢?两次握手可以嘛?为什么TCP客户端最后还要发送一次确认呢?
主要是:无法确认客户端的接收能力
分析如下:
如果是两次,你现在发了SYN=1报文想握手,但是这个包 滞留 在了当前的网络中迟迟没有到达,TCP以为这是丢了包(于是重传,然后两次握手建立好了连接)
看似没有问题,但是连接关闭后,如果这个 滞留在网络路由中的包到达了服务器呢?
这时候由于是两次握手,服务器只要接收到然后发送相应的数据包,是默认建立连接,但是现在客户端已经断开了(看到问题了吧,这就是带来的连接资源浪费)
总结下来就是一句话:主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
为什么不是四次握手?
三次握手主要是为了确认双方的 发送和接收 的能力,那四次握手可以嘛?
当然可以,100次都可以。但为了解决问题,三次就足够了,再多用处就不大了。
3.四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
1.客户端进程发出连接释放报文,并且停止发送数据。释放数据首部,FIN=1,序列号为seq=u(等于前面已经传送过来的数据的最后一个字节加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。
TCP规定,FIN报文段即使不携带数据,也要消耗一个序号
2.服务器收到连接释放的报文,发出确认报文,ACK=1,ack=u+1,并且带上字节的序列号seq=v,此时,服务器就进入了CLOSED-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据发送了,带上服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3.客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后数据)
4.服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认
5.客户端接收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,当客户撤销相应的TCB(传输模块)后,进入CLOSED(关闭)状态
6.服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些.
为什么客户端要等待2MSL?
如果不等待会怎样?
如果不等待,客户端直接跑路,当服务端还有很多数据包要给客户端发,且还在路上的时候,若客户端的端口此时刚好被新的应用占用,那么就接收到了无用数据包,造成数据包混乱。所以,最保险的做法是等服务器发来的数据包都死翘翘再启动新的应用。
那,照这样说一个 MSL 不就不够了吗,为什么要等待 2 MSL?
- 1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端
- 1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达
这就是等待 2MSL 的意义。
为什么是四次挥手而不是三次?
因为服务端在接收到FIN, 往往不会立即返回FIN, 必须等到服务端所有的报文都发送完毕了,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。
如果是三次挥手会有什么问题?
等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
参考:
两张动图-彻底明白TCP的三次握手与四次挥手(https://blog.csdn.net/qzcsu/article/details/72861891)
(建议收藏)TCP协议灵魂之问,巩固你的网路底层基础(https://juejin.im/post/5e527c58e51d4526c654bf41#heading-11)
TCP详解(https://blog.csdn.net/sinat_36629696/article/details/80740678)
TCP协议中的URG和PSH(https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/6506157.html)
TCP的几个状态 (SYN, FIN, ACK, PSH, RST, URG)(https://blog.csdn.net/lamb7758/article/details/89145453)