一、TCP协议格式
1.报头的含义
(1)源端口号/目的端口号
自己的端口号 和 对方的端口号
(2)4位首部长度
表示报头长度(TCP报头总长度 = 4位首部长度 * 4字节);报头最少有20字节
TCP报头总长度 -> 0000 ~ 1111 -> [0, 15] * 4 -> [0, 60] -> [20, 60] -> 0101 ~ 1111
(3)32位序号/32位确认序号
TCP将每个字节的数据都进行了编号 (TCP双方的地位对等,所以只需要搞定一个朝向的通信程)
32位序号:本报文段所发送数据的第一个字节的序号 (作用:标识发送的数据)
32位确认序号:告诉发送方我已经收到了哪些数据,并告知对方下一次期望你从哪里开始发
例如:确认号为N+1,则证明到序号N为止的所有数据都已正确收到,下一次你要从N+1开始发
两点注意事项
① 为什么要同时有 32位序号 / 32位确认序号,只有一个序号不行吗?答:保证全双工
② TCP收发消息大概率是并发执行的,那接收方怎么保证接收的顺序?答:根据序号排序
(4)16位窗口大小
(5)6个标志位
首先要知道:TCP报文也是有类型的,接收方要根据收到的不同类型的TCP报文做出不同的动作
所以6个标志位的作用就是标识不同类型的TCP报文
① SYN:连接请求/连接接收的报文(SYN=1为有效)
② FIN:通知对方本端要关闭(FIN=1为有效)
③ ACK:确认应答(ACK=1为确认应答,此时确认序号有效)
④ PSH:用于催促接收方,让上层尽快取走数据(PSH=1为有效)
⑤ URG + 16位紧急指针(URG=1为有效;并且URG要配合16位紧急指针来使用)
作用:告诉系统此报文段有紧急数据,应该尽快传送,而不按照原来的顺序来传送 (让数据插队)
⑥ RST:RST=1时表示TCP连接出现异常,必须强制断开链接
2.解包/分用
如何解包:通过4位首部长度得到报头长度,剩下的就是有效载荷
如何分用:通过目的端口号,就可以找到应用层的进程了,数据就可以交付给进程
二、TCP的策略
1.确认应答机制
① 为什么网络传输时会存在不可靠问题?答:就是因为距离太长了
② 不可靠问题的常见场景?答:丢包、乱序、校验错误、重复收到同一个报文…
要理解TCP的可靠性,选择确认应答机制作为切入点 (保证可靠性的策略很多,这只是其中一个)
TCP通过确认应答实现可靠的数据传输。当发送端将数据发出之后会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。反之数据可能丢失。
2.超时重传机制
如果发生了丢包 -> TCP的策略:超时重传机制,那发送方如何判定丢包了呢?
其实到底有没有丢包,发送方根本不知道。只要一定时间内没有等到确认应答(超时),发送端就可以认为数据已经丢失(丢包),并进行重发(重传)
① 超时时间怎么定?是固定的吗?答:超时时间是浮动的,根据网络情况来做调整
② 发送方发出去的数据可能重传,所以不会发送出去就立马删除,而是必须被维持一段时间
3.连接管理机制
连接管理机制:三次握手四次挥手⚠️TCP为什么要连接?因为要保证可靠性
(1)建立连接
① 一次握手行不行?不行,server会维护自已经建立好的连接,建立连接如果太简单的话,单机很容易就能攻击server(发送大量请求) -> 会导致SYN洪水
② 两次握手行不行?不行,理由同上。建立连接如果太简单的话,单机很容易就能攻击server(发送大量请求) -> SYN洪水
③ 三次握手可以:用最小成本验证全双工通信信道是通畅的;三次握手可以有效防止单机对服务器进行攻击
④ 四次握手行不行?已经验证了三次握手是没问题的了,所以在用四次握手就是浪费资源
即使是三次握手也不能完全避免SYN洪水问题。但是服务器收到攻击,本身就不是TCP该解决的
但是一次握手和二次握手有明显的漏洞,所以应该避免掉(你有明显漏洞,那就是你的问题了)
(2)断开连接
① TCP怎么知道用户把数据发完了呢?
② 主动断开连接的一方,最终状态是TIME_WAIT状态;被动断开连接的一方,两次挥手完成,会进入CLOSE_WAIT状态
③ 如果我们的服务器出现了大量的CLOSE_WAIT,原因?
a.服务器有bug,没有做close文件描述符的动作小
b.服务器有压力,可能一直给Client推送消息,导致来不及close
④ 四次挥手动作已经完成,但是主动断开连接的一方要维持一段时间的TIME_WAIT?
a.维持多长时间?等待两个MSL(maximum segment lifetime)
b.为什么要维持?保证最后一个ACK尽可能的被对方收到、保证滞留报文进行消散
⑤ 服务器有时可以立即重启,有时候无法立即重启 — bind error
因为是服务器主动断开的,所以服务器要去维持TIME_WAIT状态,维持该状态期间,该端口依旧被占用,所以就无法绑定成功
4.流量控制
流量控制 — TCP在发送数据的时候,速度必须合适;TCP利用滑动窗口机制实现流量控制
(1)正确的理解滑动窗口
滑动窗口里面装的是刚发出的数据,那么刚发出去的数据多大?-> 报头中的窗口大小告诉你!
我能发多少数据就发多少数据。所以:滑动窗口大小 = 对方通告给我的接收能力大小 (不完整)
(2)滑动窗口的实际结构
滑动窗口只会向右滑动,不会向左滑动;所以万一滑出去了怎么办?
实际上:发送缓冲区被内核组织成了环形结构,所以永远不会出现空间不够的情况
5.拥塞控制
上面所有的策略都是端到端的;丟包的时候,除了接收方出向题,网络也可能出问题!
为了处理因网络情况发送的问题,又引入了拥塞控制 -> 专门来控制能在网络中传输的数据大小
拥塞窗口中记录能在网络中传输的数据大小;如何得到拥塞窗口大小:慢开始和拥塞避免算法
所以滑动窗口的大小不应该仅仅考虑对方能收多大的数据,还要考虑当前网络能发多大的数据
滑动窗口的大小 = min{拥塞窗口大小,对方告诉我的16位窗口大小} (完整版滑动窗口大小)
6.延迟应答
7.捎带应答
在确认应答ACK的同时,也发送消息(提高效率)
三、总结TCP策略
TCP有这么多策略的原因就是要保证可靠性,同时又要尽可能的提高性能
保证可靠性:校验和、序列号、确认应答、超时重传、连接管理、流量控制、拥塞控制
提高性能:流量控制、拥塞控制、延迟应答、捎带应答
四、TCP特点
1.有连接
连接管理机制实现:三次握手四次挥手
2.可靠性
TCP的一系列策略:校验和、序列号、确认应答、超时重传、连接管理、流量控制、拥塞控制
3.面向字节流
写100个字节数据时:可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节
读100个字节数据时:既可以一次read 100个字节, 也可以一次 read一个字节, 重复100次
为什么TCP报头没有报文长度?TCP是面向字节流的 -> TCP报文解包分用后让上层自己分包
因为TCP面向字节流的特点,所以产生了粘包问题,解决方案:明确两个应用层报文的边界
①定长 ②特殊符号 ③自描述方式