目录
1. TCP 的报文格式

( 书本上的 TCP 报文段 的格式 如上如 )
1.1 源端口 、目的端口
这个与 UDP 中的源端口和目的端口 是一样的 (详见[ 网络原理 ]UDP协议-CSDN博客)
1.2 序号 、确认号
见 3.1 确认应答。
1.3 4位首部长度
TCP 的报头长度 看似这里只给了 4位首部长度 ,把 TCP 报文长度固定住了,实则不是; TCP 的报头长度是不固定的, 这与 后面的选项有关,当我们选项不选时,报头长度最短就是 20字节;当 我们 选项 选满时,报头最长是 60字节(选项最多是 40字节)。
那我们只有 4位数 来表示 报头长度 ,4位二进制数最多表示 15 ,所以我们这里的 单位是 4字节。
1.4 保留6位
UDP 有个问题就是 长度限制在 64kb,TCP就留了个小心机,我搞个保留位,先占个位置,后面如果有需要,在进行拓展,使用。
1.5 窗口大小
见 3.4滑动窗口
1.6 校验和
与 UDP 相同(详见[ 网络原理 ]UDP协议-CSDN博客)
2. TCP 的相关特性
2.1 有连接的
TCP 与 UDP 不同,TCP需要通信双方在数据传输之前,必须先建立一个连接,确保数据能够可靠地在网络中传输。
2.2 面向字节流
TCP将数据视为一连串的字节流,而不是一系列的消息。这意味着消息的边界不会在TCP头中被保留,应用程序必须自己处理消息的分割和重组。
2.3 全双工
TCP允许数据在两个方向上同时传输,即所谓的全双工通信。这意味着在一个TCP连接中,任何一端都可以同时发送和接收数据。
2.4 可靠传输( 核心)
TCP通过一系列的机制来确保数据的可靠传输。
3. 可靠传输
TCP 旨在 可靠传输,为此,TCP构建了一系列机制来确保可靠传输,下面我们来看看都有哪些机制。
3.1 确认应答
发送方:将数据发给接收方之后,接收方收到数据就会给发送方发送一个 应答报文(ack),发送方如果 收到了这个应答报文,就知道自己发送成功了。

但是由于网络中传输数据有可能 会出现“先发后至”的情况,此时,当你连续发多条信息的时候,就不能知道哪条信息(ack)是哪个消息的答复,就可能会出现下述情况:

所以,在 TCP 报文中就有 一个专门用来标记的信息的发送顺序:

这里 TCP 需要完成两个工作:
1. 确保应答报文和发出的数据能够对上号
2. 确保在出现先发后至的情况时,应用程序仍然能按照正确的顺序来理解数据
那如何理解 32位序号以及32位确认序号呢,见下图:

注意:
如何区分一个数据包是 普通的数据包,还是 ack的应答数据呢?
此时我们就需要继续看看 TCP 的报头结构了:
在报头结构中,当 ack 位置 为1,表示当前数据包是一个应答报文,并且数据包中的 确认序号字段就会生效;如果为 0,则是一个普通数据包,确认序号不会生效。
3.2 超时重传
确认应答 是一种比较理想的情况,那如果网络上丢包了,发送方就收不到 ack 了,那连接不就建立不起来吗?
所以,我们又 增加了超时重传的机制,针对 确认应答进行补充。
丢包情况 无非两种: 一种是传输的数据丢了, 另一种就是 返回的 ack 丢了:

无论发生上述哪种情况,发送方都会进行 “重新传输”。
假设丢包的概率 为 10%,那重传之后,两次丢包概率 就变成了 1%
所以说,重传操作大大提升了 数据能够被传过去的概率
注意:
1. 那发送方何时 进行重传呢?
其实它是有一个等待时间的,当发送方 发出数据一段时间之后,如果在这段时间内,收到 ack ,就视为 数据到达,如果没有收到 ack ,就会视为丢包(可能是数据,也可能是ack),此时就会触发重传机制。
2. 那 如果 接收方 突然断开网络,那发送方 会一直 重传吗?
当然不会,当重传第一次 还是没有收到,此时第二次等待的时间会比第一次更长,时间拉长也不是无限拉长,当达到一定程度时,就会认为无论 重发 多少次,都不可能到达,此时就会放弃 tcp 连接了。
3. 当 ack 丢失的时候,发送方就会重传,那接收方不就收到相同的数据了吗,那怎么处理呢?
其实 TCP 已经很贴心的帮我们将这个问题给解决了, TCP 会有一个 接收缓存区 ,也就是一个内存空间,会保存当前已经收到的数据,以及数据的序号,当发送数据重复时, 接收方就会直接把后来的数据给丢弃,确保应用程序 在读取数据的时候,不会读到同一个数据多次。
3.3 连接管理
(1)建立连接 --- 三次握手
这里的三次握手,只是打招呼,并没有实际意义,只是为了唤起对方的注意
这里的三次握手就好比于:

在 TCP 中,“ 三次握手 ”发送的数据包 并不是 真正要发送的数据,而是一个 特殊的 TCP 数据报,它在 TCP 的报头中有 显示(syn 位为1 ,表示 是同步报文段;为0,则不是):
所以上述 握手就可以 看成这样:

综上, 三次握手的 作用 有这些:
(1) 确保当前网络 是否通畅
(2) 让 发送方和接收方 都能确认自己的发送能力和接收能力 均正常
(3) 让通信双方在握手的过程中,协商一些重要的参数(比如 tcp 的序号从哪开始)
(2) 断开连接 --- 四次挥手
断开连接 本质上跟 建立连接差不多。

这里 主机 A 跟 主机B 说 它要断开连接 ,其实是 发送了 一个 结束报文段(FIN):
那 这里能不能跟 上述建立连接一样,将 FIN 和 ACK 一起发送过去 呢?
答案显然是不行的,首先 在建立连接的时候,ACK和SYN 都是内核触发的,属于同一个时机,所以可以合并在一起;而这里 断开连接,FIN 是 应用程序执行 完 close 之后触发的,实际并不相同,所以不能合并成 三次挥手。
注意:
发送方A 是否收到 接收方B 发送的 FIN 就会断开连接呢?
答案显然 不会。你看,如果 发送方A 接收到 接收方B 发送的 FIN 就直接断开连接了,ACK 恰好丢包了,那 接收方B 就会接收不到 ACK ,就会在一段时间之后触发 超时重传,但是 发送方A 已经断开连接了,那 接收方B 就一直 触发超时重传,但是 B 永远也就收不到 ACK 了。
所以,当 A 收到 FIN 之后,会先处于 TIME_WAIT 状态,并且等待 2MSL( 网络上两节点通信消耗的最大时间为 MSL)之后,如果还没有收到 B 超时重传的 FIN ,此时 A 才会断开连接。
如下图:

前面三个机制,都是在确保 tcp的可靠性,但是,提升可靠性的同时,效率却降低了,为让 可靠传输对性能的影响更小一些,就引入了 滑动窗口 。
3.4 滑动窗口
刚才我们讨论了确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送 下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多 个段的等待时间重叠在一起了)。

1. 窗口大小 指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是 4000 个字节(四个段)。2. 发送前四个段的时候,不需要等待任何 ACK ,直接发送;3. 收到第一个 ACK 后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;4. 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有 应答;只有确认应答过的数据,才能从缓冲区删掉;5. 窗口越大,则网络的吞吐率就越高;

那么如果出现了丢包,如何进行重传?
情景一: 数据包没丢,应答报文 ACK 丢了

这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认;
情景二: 数据包丢了

(1)当某一段报文段丢失之后,发送端会一直收到 1001 这样的 ACK ,就像是在提醒发送端 " 我想 要的是 1001" 一样;(2)如果发送端主机连续三次收到了同样一个 "1001" 这样的应答,就会将对应的数据 1001 - 2000 重新发送;(3) 这个时候接收端收到了 1001 之后,再次返回的 ACK 就是 7001 了(因为 2001 - 7000 )接收端 其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区 中;
3.5 流量控制
(1) 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 " 窗口大小 " 字段,通过 ACK 端通知 发送端;(2)窗口大小字段越大,说明网络的吞吐量越高;(3)接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端; 发送端接受到这个窗口之后,就会减慢自己的发送速度;(4) 如果接收端缓冲区满了,就会将窗口置为0 ;这时发送方不再发送数据,但是需要定期发送 一个 窗口探测数据段 ,使接收端把窗口大小告诉发送端。
3.6 拥塞控制
3.7 延时应答
(1)假设接收端缓冲区为 1M 。一次收到了 500K 的数据;如果立刻应答,返回的窗口就是 500K ;(2)但实际上可能处理端处理的速度很快, 10ms 之内就把 500K 数据从缓冲区消费掉了;在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;(3)如果接收端稍微等一会再应答,比如等待 200ms 再应答,那么这个时候返回的窗口大小就是 1M;
数量限制:每隔 N 个包就应答一次;时间限制:超过最大延迟时间就应答一次;
3.8 捎带应答
3.9 面向字节流
由于 TCP 在数据传输的时候是面向字节流的,接收方在接受的时候会先创建一个接收缓存区,将 多个 应用层的数据包的数据,以字节的形式 仅仅 挨在一起,那 接收方 的应用程序在 读取数据的时候,由于 所有的数据包的数据都紧挨在一起,接收方就无法 判断 从哪个字节到哪个字节是一个完整的 数据包了。这就是 粘包问题。
粘包问题
粘包问题就是,接收方无法判断 每个数据包 的边界,那么要解决粘包问题,就给它规定好边界即可。
1. 引入 分隔符

2. 引入长度

3.10 异常处理
(2)主机关机

被折叠的 条评论
为什么被折叠?



