TCP协议的相关特性
目录
(一)可靠传输
核心机制:
①确认应答
方案
②超时重传
如何判断超时?
如何判断是 发送方 数据丢包还是 接收方 数据丢包?
③连接管理
如何建立连接?
为什么要三次握手?
TCP状态变化
如何断开连接?
为什么以上中间两步不合并?
为什么合并?(特殊情况)
为什么要四次挥手?
(二)提高效率
核心机制:
①滑动窗口
出现丢包,怎么重传?
1.ack丢包
2.数据包丢包
②流量控制
怎么衡量接收方的“处理能力”?
具体实现方案
③拥塞控制
解决思想
具体实现方案
④延时应答
⑤捎带应答
1.机制
2.注意点
⑥面向字节流
(1)粘包问题
(2)解决方案(让包边界更清晰)
⑦异常情况
(1)进程奔溃
(2)主机关机(正常关机)
(3)主机掉电(拔电源)
(4)网线断开
(一)可靠传输
-
可靠传输不能保证数据100%传达接收方。
-
这里的“可靠传输”:知道对方是否收到数据。
核心机制:
①确认应答
以对方的“应答报文(acknowledge,ack)”判断对方是否收到。
针对问题:先发后至
方案
对于每次传输都标上序号:数据部分第一个字节的编号。
(在同一个TCP连接内,序号会持续累加,
下一个数据报的序号在上一个数据报最后一个字节的序号基础上累加)
和确认序号:只在应答报文中生效(ACK为1,生效),
在数据报最后一个字节的序号基础上加一
缓冲接受区:数据重排
②超时重传
针对问题:丢包
如何判断超时?
设定超时时间,该超时时间内未收到ack,即可判断。
超时时间:动态变化,随着重传次数增多而增多。如果 超时时间 累加到超过上限,则 tcp连接 被重置(单方面断开连接)
RST复位报文:被触发,表示丢弃连接(删除保存的对方信息)
如何判断是 发送方 数据丢包还是 接收方 数据丢包?
缓冲接受区:如果 收到信息编号 == 已有信息编号,则去重。
③连接管理
如何建立连接?
三次握手(HandShake):
1.客户端给服务器发起一个syn(同步报文段)。
(标志位SYN为1)
2.服务器给客户端返回一个ack(应答报文)。
2.服务器给客户端发起一个syn。
3.客户端给服务器返回一个ack。
为什么要三次握手?
保证“可靠传输”的辅助手段:
-
验证通信链路是否通畅。
-
验证通信双方通信能力是否正常。
-
通过比较序号来判断“数据报”是否是上个连接发来的,是则丢弃。
(三次握手需要通信双方协商 起始序号)
TCP状态变化
1.LISTEN
服务器存在的状态。
服务器 new ServerSocket 就会进入 LISTEN 这个端口,端口上过来客户端,就能直接accept。
2.ESTABLISHED
客户端和服务器连接已经建立好了,可以进行通信。
3.CLOSE_WAIT
被动断开的一方进入的状态。服务器收到客户端发来的FIN,就会返回ACK,同时进入CLOSE_WAIT状态。
4.TIME_WAITING
有时间限制的等待。进入时机:主动发起断开连接的一方,进入该状态。
状态原因:等最后一个ack发送成功。
如何断开连接?
四次挥手(WavaHand):
1.客户端(服务器)向服务器发起一个 FIN结束报文。
(标志位FIN则为1)
2.服务器回复一个ack。
3.服务器向客户端发起一个 FIN结束报文。
4.客户端回复一个ack。
为什么以上中间两步不合并?
-
四次操作并不是完全由操作系统内核完成,而是和应用层代码有关。
-
如果应用层代码执行滞后了,就会导致第2,3步合并失败。
为什么合并?(特殊情况)
-
延时应答:在eg中,服务器发送ack的时机,会往后移一段时间;再加上服务器收到ack触发close之间时间间隔不长。这两条数据就可以合并了。
-
合并可以提高效率,因为 封装分用 都是有开销的。
为什么要四次挥手?
为了释放空间。
(二)提高效率
核心机制:
①滑动窗口
既然这样⼀发⼀收的⽅式性能较低, 那么我们⼀次发送多条数据, 就可以⼤⼤的提⾼性能。
(其实是将多个段的等待时间重叠在⼀起了)
收到第⼀个ACK后, 滑动窗⼝向后移动, 继续发送下一段的数据。
操作系统内核为了维护这个滑动窗⼝, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉。
窗口大小:
⽆需等待确认应答⽽可以继续发送数据的最⼤值。根据 流量控制 和 拥塞控制 共同决定(取较小值)。
出现丢包,怎么重传?
1.ack丢包
-
如果不是最后一个ack,就不采取任何措施。
-
如果是最后一个ack,就采取“超时重传”。
2.数据包丢包
-
快速重传:发送端收到连续三次相同ack,则重传发送失败的数据包。
-
快速重传:接收方收到数据包后,因为 接收缓冲区 中已经有了前几个数据包,就直接从最后一个数据包 最后一个字节的编号 开始返回 确认序号。
-
超时重传:长时间没有收到ack,则触发。
②流量控制
根据接收方的处理能力,干预到发送方的发送速度(调整滑动窗口大小)。
(窗口大小太大,发送速度太快,会导致接收方处理不过来)
怎么衡量接收方的“处理能力”?
1.接收方调用 read() 的速度。
(read接收到的一定是有序数据)
接收缓冲区:看成“阻塞队列”
2.接收(receive)缓冲区 剩余空间 的大小。
具体实现方案
把 接收缓冲区 剩余空间的大小填入 ack 的报头(16位窗口大小)中。
当ack中接收缓冲区大小为零,则发送方暂停发送。
服务器会在 接收缓冲区大小 为零的时候,发送“窗口探测包”。
窗口探测包:不携带 业务数据(TCP载荷部分非空的数据,携带应用层数据包的数据)
客户端在 接收缓冲区 有空闲位置后,就发送“窗口更新通知”。
特殊情况:如果 窗口更新通知 发送失败,则会导致无法通信,使用服务器需要定期发送 窗口探测包。
③拥塞控制
限制滑动窗口的发送速率(考虑传输路径)。
解决思想
先以较小速度发送数据,如果丢包,说明中间有节点出错,则减小窗口大小;
如果不丢包,则以较大速度发送数据,如果丢包,则减小窗口大小。达成“动态平衡”。
具体实现方案
1.慢启动(因为网络情况未知)。
2.如果不丢包,则指数式增长。
3.指数达到阈值后,则线性增长。
4.线性增长到一定程度,则触发丢包。
5.Ⅰ.出现丢包,回到慢启动(与此同时,指数阈值 和 线性阈值 改变)。
5.Ⅱ.快恢复:出现丢包,重新计算阈值(丢包窗口大小除以2),从阈值开始作为新的拥塞窗口,继续线性增长。
④延时应答
-
在保证⽹络不拥塞的情况下尽量提⾼传输效率。
-
应对接收端处理还远没有达到⾃⼰的极限的情况。
⑤捎带应答
1.机制
-
计算响应response是需要时间的,又由于延时应答,ack的发送也需要等待。
-
response就可以和ack合成一个数据包发送。
2.注意点
-
捎带应答 既取决于延时应答,又和应用程序的处理逻辑有关。
-
捎带应答不是100%触发的,但是TCP会尽可能触发。
⑥面向字节流
(1)粘包问题
1.发送方发送 TCP报头+应用层载荷1,TCP报头+应用层载荷2。
2.接收方处理,去掉报头,把载荷内容放到 接收缓冲区。
3.接收方应用程序,read的时候,有很多种read的可能性。
(这取决于应用层代码怎么写)
——所以怎么读才能确保应用层程序读到的是一个“完整的应用层数据包”?
——怎么读才能不把所有的载荷都混在一起,分辨不清?
(2)解决方案(让包边界更清晰)
通过引入特殊的分隔符,来作为包的边界。
(我们写的回显服务器,分隔符就是“空白符”)
在应用层数据包开头的地方,通过固定长度,来约定整个数据包的长度。
⑦异常情况
(1)进程奔溃
-
对应的文件描述符表被关闭。
-
调用close,释放PCB。
-
TCP的连接还会保留一会,不会立即结束。
(2)主机关机(正常关机)
-
先结束所有进程。
-
触发FIN,进入四次挥手。
-
对端正常返回ack,发送FIN。
-
对端等不到主机返回的ack,就可能直接删除连接。
-
(如果挥不完)主机可以保证自己删除对端信息。
(3)主机掉电(拔电源)
-
如果掉电的一方是接收方,发送方收不到ack,则会触发 超时重传,重传到一定程度后,发送方发送一个复位报文,表示要重置连接,没有收到回应则释放连接。
-
如果掉电的一方是发送方,接收方阻塞等待发送方的数据包。接收方周期性和发送方交换“心跳包”,此时接收方收不到发送方的“心跳包”,则单方面释放连接。
(4)网线断开
-
本质上和 (3)主机掉电 是一个流程。