[趣谈网络协议学习] 11 TCP协议(上):因性恶而复杂,先恶后善反轻松

TCP 包头格式

TCP 头的格式如图:

TCP 包头格式

可参看:TCP协议以及其报头结构分析TCP 包头详解

  • 16位端口号:告知主机该报文段是来自哪里(源端口)以及传给哪个上层协议或应用程序(目的端口)。
  • 32位序号:一次TCP通信过程中的一个传输方向上的字节流的每个字节的编号。
  • 32位确认号:用作对另一个发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1.假设主机A和主机B进行TCP通信,那么A发送出的TCP报文段不仅携带自己的序号,而且包含对主机B发送来的TCP报文段的确认号。反之,B发出的TCP报文段也同时携带自己的序号和对A发送来的报文段的确认号。
  • 4位首部长度:标识该TCP头部有多少个32bit(4字节),四位最大表示15,所以TCP报头最长是60字节。
  • 6位标志位 :
    • URG:表示紧急指针是否有效。
    • ACK:表示确认号是否有效。我们称携带ACK标志的TCP报文段为确认报文段。
    • PSH:提示接收端应用程序应该立即从TCP接受缓冲区中读走数据,为接受后续数据腾出空间,如果不将接收到的数据读走,它们就会一直停留在TCP报文段。
    • RST:表示要求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段。
    • SYN:表示请求建立一个新连接。称携带SYN标志位的TCP报文段为同步报文段。
    • FIN:表示通知对方本端要关闭连接了。称携带FIN标志的TCP报文段为结束报文段。
  • 16位窗口大小:这是TCP流量控制的一个手段。这里的窗口指的是接受通告的窗口。告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样就对方就可以控制发送数据的速度。
  • 16位校验和:由发送端填充,接收端对TCP报文端执行CRC算法以检验TCP报文段在传输过程中是否损坏(检验部分包括报头和数据部分)。
  • 16位紧急指针:为一个正的偏移量。它的序号字段的值相加表示最后一个紧急数据的下一个字节的序号。即紧急指针相对于当前序号的偏移。
  • 剩下40字节为选项字段。

序号解决包的乱序问题。

确认序号确认对方收到,没有收到则重新发送。

状态位维护连接的状态。SYN发起连接,ACK回复,RST重新连接,FIN结束连接。

窗口大小用于流量控制。

通过对 TCP 头的解析,要掌握 TCP 协议,重点应该关注以下几个问题:

  • 顺序问题 ,稳重不乱;
  • 丢包问题,承诺靠谱;
  • 连接维护,有始有终;
  • 流量控制,把握分寸;
  • 拥塞控制,知进知退。

TCP 的三次握手

TCP 的连接建立,我们常常称为三次握手。

	A:您好,我是 A。
	B:您好 A,我是 B。
	A:您好 B。
	B收到A发来的 您好 B。三次握手完成

常称为“请求 -> 应答 -> 应答之应答”的三个回合。

什么要三次,而不是两次?为了可靠,为什么不是四次?

两次握手:存在A的请求连接B的报文在网络中滞留,延误到连接释放后才到达B,B以为是A新发起的连接请求,同意建立连接,浪费了资源:端口、内存等。

三次握手可防止上述情况发生,因为A不确认,B就不会建立连接。四次握手是可以的,四十次都可以,关键四百次也不能保证就真的可靠了,三次握手只要双方的消息都有去有回,就基本就认为连接可以了。

大部分情况下,A和B建立连接后,A会马上发数据。如果不发,B可以开启keepalive,发送探活包。B也可以对于长时间不发包的客户端,主动关闭。

三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是TCP 包的序号的问题。

A 要告诉 B,我这面发起的包的序号起始是从哪个号开始的,B 同样也要告诉 A,B 发起的包的序号起始是从哪个号开始的。为什么序号不能都从 1开始呢?因为这样往往会出现冲突。

TCP三次握手状态机

三次握手状态机

	1、客户端和服务端都处于 CLOSED 状态。
		客户端: CLOSED	·服务端: CLOSED
	2、服务端主动监听某个端口
		客户端: CLOSED	·服务端: LISTEN 
	3、客户端主动发起连接 SYN
		客户端: SYN-SENT 	·服务端: LISTEN 
	4、服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN
		客户端: SYN-SENT 	·服务端: SYN-RCVD
	5、客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK
		客户端: ESTABLISHED 	·服务端: SYN-RCVD
	6、服务端收到 ACK 的 ACK 之后
		客户端: ESTABLISHED 	·服务端: ESTABLISHED
	此时客户端服务端都是一发一收了

TCP 四次挥手

好说好散,这常被称为四次挥手。

	A:B 啊,我不想玩了。
	B:哦,你不想玩了啊,我知道了。
	B:A 啊,好吧,我也不玩了,拜拜。
	A:好的,拜拜。

整个连接就关闭了,上面是和平分手的场面,但也会有异常

异常1:A 说完“不玩了”之后,直接跑路,会导致问题,因为 B 还没有发起结束,而如果 A 跑路,B 就算发起结束,也得不到回答,B 就不知道该怎么办了。

异常2:A 说完“不玩了”,B 直接跑路,也是有问题的,A 不知道 B 是还有事情要处理,还是过一会儿会发送结束。

为了解决这些问题,TCP 协议专门设计了几个状态来处理这些问题。如下图状态时序图。

TCP四次挥手状态机

四次挥手状态机

	1、初始状态A、B均处于 ESTABLISHED 状态。
		A: ESTABLISHED  B: ESTABLISHED
	2、A 说“不玩了”
		A: FIN_WAIT_1   B: ESTABLISHED
	3、B 收到“A 不玩”的消息后,发送知道了到A
		A: FIN_WAIT_1   B: CLOSE_WAIT 
	4、A 收到“B 说知道了”
		A: FIN_WAIT_1    B: CLOSE_WAIT 
		如果这个时候 B 直接跑路,则 A 将永远在这个状态。TCP 协议里面并没有对这个状态的处理,但是 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。
		
	5、如果 B 没有跑路,发送了“B 也不玩了”的请求到达 A 
		A: FIN_WAIT_2    B: LAST-ACK

	6、A 发送“知道 B 也不玩了”的 ACK
		A: TIME-WAIT(等待2MSL)    B: LAST-ACK
		按说 A 这时可以跑路了,但是直接跑路有问题:
		
		问题1 :最后的这个 ACK 万一 B 收不到呢?
		则 B 会重新发一个“B 不玩了”,这个时候 A 已经跑路了的话,B 就再也收不到 ACK 了,因而 TCP 协议要求 A最后等待一段时间 TIME_WAIT,这个时间要足够长,长到如果 B 没收到 ACK 的话,“B 说不玩了”会重发的,A 会重新发一个 ACK 并且足够时间到达 B。
		
		问题2 :A 直接跑路,A 的端口就直接空出来了,但是 B 不知道,B 原来发过的很多包很可能还在路上,如果 A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中 B 发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱,因而也需要等足够长的时间,等到原来 B 发送的所有的包都死翘翘,再空出端口来。
		
	7、B 收到 A 发送“知道 B 也不玩了”的 ACK,并且 A 也等够 2MSL
		A: CLOSED    B: CLOSED

MSL是Maximum Segment Lifetime,报文最大生存时间它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

还有一个异常情况:B 超过了 2MSL 的时间,依然没有收到它发的 FIN 的 ACK,怎么办呢?

按照 TCP 的原理,B 当然还会重发 FIN,这个时候A 再收到这个包之后,A 就表示,我已经在这里等了这么长时间了,已经仁至义尽了,之后的我就都不认了,于是就直接发送 RST,B 就知道 A 早就跑

TCP 状态机

将连接建立和连接断开的两个时序状态图综合起来,就是这个著名的 TCP 的状态机

TCP 状态机
加黑加粗的部分,是上面说到的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户端 A 的状态变迁,加粗的虚线是服务端 B 的状态变迁。

小结

TCP 包头很复杂,主要关注五个问题,顺序问题,丢包问题,连接维护,流量控制,拥塞控制;
连接的建立是经过三次握手,断开的时候四次挥手,状态图

参考资料:

趣谈网络协议(极客时间)链接:
http://gk.link/a/106nW

TCP协议以及其报头结构分析:
https://blog.csdn.net/MBuger/article/details/74078777

TCP 包头详解:
https://blog.51cto.com/13854765/2163296


GitHub链接:
https://github.com/lichangke/LeetCode
知乎个人首页:
https://www.zhihu.com/people/lichangke/
CSDN首页:
https://me.csdn.net/leacock1991
欢迎大家来一起交流学习

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值