目录
- 可靠传输有啥用
- 确认应答
- 超时重传
- 总结
可靠传输有啥用
我们知道相比于 UDP, TCP 的传输是可靠的, 啥意思呢?
就是 UDP 发送的数据, 它自己不知道发送的数据对方是否接收到.
而 TCP 发送的数据, 它知道对方是否接收到, 也就是说对方会给个应答.
假设一个场景 :
甲要付款给乙, 甲的账户上明明扣了款, 但是乙却迟迟没收到, 那甲还要付款吗? 甲的账户里已经扣了钱, 再付款就是冤大头了.
TCP 存在的初心就是为了保障数据传输的可靠性, 让这种情况不再发生.
那 TCP 具体是如何实现可靠传输的呢?
有两点 :
- 确认应答
- 超时重传
确认应答
确认应答是实现可靠性的最核心机制.
我们可以观察TCP报文结构 :
其实 TCP 针对将要发送的数据都进行一个字节一个字节的编号, 即为序列号.
对于发送方来说, 确认序号无意义, 只有在ack中也就是应答报文中才有意义.
为了更好理解, 就将数据传输简化如下 :
确认序号是取发送过来的所有数据的最后一个字节的下一个字节的序号.
确认序号 1001 的含义 :
- < 1001 的数据我已经收到了.
- 接下来向发送方索要从 1001 开始的数据.
接收方可以通过 ack 的确认序号, 来告诉发送方哪些数据已经收到了.
在数据传输的过程中, 也可能出现 “先来后到” 的情况.
比如 : 客户端连续发了两条数据, 服务端先接收到数据2, 再接收到数据1, 显然这两个数据顺序反了, 如果就这样不管的话显然会出事的.
TCP 其实还有个整队的能力, TCP 会有个接收缓冲区 (一块内核中的内存空间), 每个 socket 都有一份自己的缓冲区.
TCP 将接收到的数据存储在缓冲区, 这样就可以利用序号来对数据进行整队了. (这也是 TCP 序号的一个重要用途).
这样就保证了应用程序读数据时, 读到的一定是有序的数据了.(和发送的顺序一样)
注意
应用程序读取完数据后, 缓冲区的数据就删除了.
超时重传
在数据传输过程中, 并不是一帆风顺的, 可能还会出现丢包情况.
丢包是网络上非常典型的情况.
为什么会丢包呢?
其实数据传输过程并不是简单的两个节点互动, 而是途中会经历很多节点, 就像我们看地图找目标点一样, 到达目标点可能需要经过多个十字路口.
中间任何一个节点出了问题, 都可能会出现丢包情况.
这中间的每个设备, 都在承担很多的转发任务.
每个设备的转发能力都是有上限的.
当某一时刻, 某个设备的流量达到了峰值, 就可能会出现丢包情况.
如果包丢了, 接收方就收不到了, 也就不会返回 ack, 发送方也就拿不到应答报文, 等待一段时间后, 如果还是没有收到应答报文, 发送方就知道刚才的数据包丢包了, 就会再发一遍.
丢包是个概率事件, 如果发生了丢包, 一般重传还是可以传过去的.
发送方对于丢包的判定 : 一定时间内没有收到 ack.
其实丢包也分为两种情况的 :
- 数据直接丢失, 接收方没收到, 自然不会发送 ack.
- 接收方收到数据, 但是返回的 ack 丢了.
发送方是区分不了这两种情况的, 只能都重传了.
其实这两种情况 TCP 都考虑到了, 它会在缓冲区中, 根据数据序号来进行去重, 保证应用程序读取到的数据只有一份.
那它如何应对多个包丢失呢?
还是超时重传, 只是每丢包一次, 超时等待时间都变长 (降低重传的频率), 因为 TCP 认为这种情况是网络出现严重故障, 就干脆摆烂了, 节省资源.
如果连续多次重传都没有得到 ack, 此时 TCP 就会重置连接 (也就是重连), 如果重置连接失败, TCP 就会关闭连接, 放弃网络通信.
总结
TCP 可靠传输的基石 :
- 一切顺利, 以确认应答保证可靠性.
- 出现丢包, 使用超时重传作为补充.