网络原理---TCP协议详解

TCP协议
TCP协议的可靠性

TCP协议的特点 : 有连接 , 可靠传输 , 面向字节流, 全双工

其中最重要的是可靠传输.

可靠传输 : 发送方能够知道 所发送的信息对方是否收到了

那如何实现可靠传输 ?

TCP段协议格式

image-20221109141028150

TCP的报文分为 普通报文应答报文 .

image-20221109141017753

应答报文的 中的 ACK 标记位 为1 , 普通报文 为 0.

当为 普通报文时 32 位确认序号不生效 , 如果当前报文时 应答报文,确认序号就表示应答的是哪个报文

确认应答机制

image-20221109141004877

每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开 始发。

进一步理解区分面向字节流面向数据报

面向字节流 和 面向数据报 都是在应用层中体现的 , 在传输层中, UDP 和 TCP 协议都是通过报文进行传输的.

当每一条 报文 传输过来后 , 在操作系统内核中 有一块 接收缓冲区 (内核中的一块内存空间, 每个socket都有自己的缓冲区) , 用来接收 传输过来的报文 ,

而 面向数据报时, 在接收缓冲区中 存储报文 是以类似链表的结构来存储的,每个报文都是一个节点,读取的时候也是 以节点为单位读取.

面向字节流时 , 在接收缓冲区中 存储报文 是以类似数组的结构来存储的 , 每个报文都是数组中的一个元素, 你要读取多少的元素 , 就读取多少个元素,体现的流的状态.

超时重传机制

在确认应答机制下, 能知道 告诉发送方 接收端收到了 , 但是如果没收到, 如何确认? 就需要用过超时重传机制

发送过程中 会发生 丢包 的现象, 而 丢包可能会丢普通报文, 也可能会丢 ACK ,两种情况下,发送方都不知道数据对方是否收到.

这时就会启动超时重传机制, 在一段时间内 没收到应答 ,发送方就会再发送一次数据.

如果丢的 是 ACK ,重新发送后 接收端就有两份一样的数据了,那么 就会把相同的数据丢掉一份 ,只保留一份有效数据

传输过程中 , 会经过 交换机 路由器等中间过程, 每到达一个中转位置, 都会 检查 报文的 校验和,如果发生变化,就说明发生了丢包 ,会再重新进行发送

超时重传机制的, 多久没收到应答进行重新发送的时间间隔, 会越来越长. 如 : 第一次发送没收到, 间隔1秒再发送 , 第二次发送又没收到 ,就会间隔 2 秒再发送, 以此类推. 也不会一直重新发送, 尝试几次后, 如果仍然不成功,就会断开连接,重新尝试连接. 重连也连不上,就放弃了.

确认应答机制 和 超时重传机制 是TCP可靠性的最核心机制

TCP协议的有连接
(一) 三次握手

如何建立连接 ? 通过 TCP协议的三次握手机制

image-20221109140952326

三次握手

  1. 由客户端先发送建立连接的请求( syn )
  2. 服务器收到后, 也给客户端发送确认收到的应答 (ACK)建立连接的请求(syn)
  3. 客户端 收到 服务的的 ACK 和 syn 后, 给服务器 发送一个 确认收到的 ACK , 连接建立完成

为什么要三次握手 (三次握手的意义) :

  1. 相当于"投石问路 " , 要进行发送数据前, 先测试下发送数据的线路有没有问题 . 保证没有问题再进行发送, 同时也是在验证通信双方的 发送能力接收能力 是否正常. ( 两个人 语音开黑 , 测试两人的 耳机和 麦克风)
  2. 用于通信双方进行协商 一些重要的参数.( 比如序号要从几开始)

两次行不行 ? 不行 , 因为次数太少无法确认线路是否畅通

四次行不行? 可以, 但是没必要因为降低了效率

(二) 四次挥手

四次挥手用于 断开连接 ,

image-20221109140936760

四次挥手流程: ( 客户端A 或者 服务器B 都可以主动断开连接)

  1. 客户端 向 服务器 发送 断开连接的请求( FIN)
  2. 服务器 接收到 FIN, 立即给 客户端返回ACK
  3. 服务器 中用户调用 socket.close 方法时,才会给客户端发送 FIN ,与第二步有不可忽视的时间间隔,所以 2 3 步不可以合并.
  4. 客户端收到 syn 后, 给服务器返回ACK. 连接断开

TCP建立连接时,是没有历史包袱的,立即就能完成,而 断开连接时, A发送FIN给B时, B中可能还有数据在缓冲区中没读完, B要把数据处理完了再发FIN , B什么时候发FIN就是代码层次的问题了.

TCP中的状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZfZROAl-1667974243144)(2022-8-9 2022-8-9 javaEE笔记 081053 102105.assets/image-20220810155029015.pngimage-20221109140912842]

CLOSED状态 : 连接断开( 释放)的状态

服务器 LISTEN 状态 : 服务器已经启动就绪等待连接的状态

ESTABUSHED 状态: 连接已经建立好了 ,可以进行后续通信

服务器 CLOSE WAIT 状态 : 收到了客户端 的 FIN ,返回ACK后 , 在自己调用close方法 , 发送FIN 之前的状态.

LAST_ACK 状态 : 发送 FIN后 ,未收到ACK前的状态

TIME_WAIT 状态 :

主动发起FIN的一方在 收到对方的FIN后进入的状态, 在收到对方的FIN后,要等待一段时间再进入CLOSED状态释放连接.

目的是为了防止最后一个ACK丢失 , 如果丢失 , 服务器端会认为 发起方 未收到FIN ,会进行重发 . 对于重发的FIN如果 此时A 已经释放连接, 进入CLOSED状态,就没有人处理 FIN 了 . 而 TIME_WAIT状态就是为了处理这种情况, 在TIME_WAIT状态 , 如果一段时间后没有收到 服务器 重发的FIN ,就说明此时 最后一个ACK对方已经收到了, 发起方 也进入 CLOSED状态.

TIME_WAIT 等待的时间是 2 MSL . MSL是操作系统中的一个配置参数, 表示两个主机之间,数据从一边到另一边花费的最大时间 .

为什么是 2MSL ? 因为要确保 最后一个ACK 从 A- > B , 重发的FIN 从 B->A ;

滑动窗口机制

在保证可靠性的前提下, 因为为了保证可靠性, 降低了数据传输的效率 . 为了补救传输数据的效率. 使用了滑动窗口机制 . 因此 滑动窗口是一种效率补救机制

滑动窗口机制 : 在发送数据时 , 一次性发送多条 ,相当于一个窗口, 将窗口内的数据一次性发出去, 在发送的第一条的ACK返回后, 窗口就往后滑动.继续发送后续数据.

具体工作过程如图 :

image-20221109140847539

上图中的 窗口大小是 4等分段 , 相当于一次 发送 4 条数据(1001 - 5000) , 在窗口中的第一条数据 的ACK 返回后, 就继续发送(5001 - 6000)的数据 ,看起来就像窗口在滑动.

发送数据过程中 必然会产生 丢包的问题: 在丢包的情况下, 能否保证可靠性呢. 下面分为两种情况 分析 :

  1. 丢失的是ACK

    如果丢失的是其中某条数据的ACK ,并不会影响可靠性 ,因为只要 后续数据的ACK收到 ,就说明前面的的数据都已经发送到接收端了. 因为ACK设计的特点 , 后续的ACK会涵盖前面的ACK.

  2. 丢失的是数据包

    如果丢失的是其中某个数据包( 如 1001-2000)的数据包丢失), 接收方就会一直向发送方返回 1001的ACK ,在重复几次后, 发送方就明白了 是 1001-2000的数据包丢失 , 启动重发机制 ,重新发送 1001-2000的数据包. ( 具体过程如下图)

image-20221109140837155

  • 当某一段报文段丢失之后,发送端会一直收到 1001 这样的ACK,就像是在提醒发送端 “我想 要的是 1001” 一样;
  • 如果发送端主机连续三次收到了同样一个 “1001” 这样的应答,就会将对应的数据 1001 - 2000 重新发送;
  • 这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端 其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中;

这种机制被称为 “高速重发控制”(也叫 “快重传”)。

流量控制机制

流量控制是用来控制滑动窗口的大小的, 因为滑动窗口机制提高了数据发送的效率, 如果滑动窗口越大, 发送速率越大. 但是如果发送速率过大, 而接收方的接收速率 ,是有限的, 这样就会导致接收方处理不过来, 导致丢包问题, 这样就要频繁的进行重发 , 效率反而更低. 流量控制机制是一种保证可靠性的机制

为了控制 滑动窗口的大小 ,保证效率, 引入流量控制机制.

流量控制机制:

根据接收方接收速率的大小 ,确定 滑动窗口的大小.

那么如何确定 接收方的接收速率多大呢 ?

在接收方接收数据时, 接收方的操作系统内核中 会有一块 " 接收缓冲区",先存放当前接收到的数据, 而读取接收缓冲区中的存放的数据 的速度,就是接收速率. 如图

接受速率的多快是根据我们的 所写的代码的实现方式来确定的 , 代码的实现方式 五花八门 ,就是导致接收的速率也各不相同. 直接表示并不好表示

image-20221109140619082

这时可以根据根据接收缓冲区 剩余空间的大小 表示 接受速率的大小.

可以想象成一个蓄水池( 如下图)

image-20221109140537435

那 接收方 如何接收缓冲区的大小告诉 发送方呢?

可以在ACK的报文中 带上这个信息

image-20221109140509118

拥塞控制机制

通信的双方 A 和 B 要完成通信 , 不单只有这两个 , 还有很多的中转站 ( 交换机 \ 路由器) . 此时用 接收缓冲区剩余空间的大小来衡量 接收方B 的接收速率 , 那中转站的中转能力又要如何进行衡量呢 ? 这就是拥塞控制

拥塞控制 :

做实验 ( 定性分析)

  1. 先以最小的 滑动窗口 来试探,
  2. 如果不丢包 , 说明网络顺畅, 逐步增大滑动窗口
  3. 放大到一定程度后,速率已经比较快了, 网络出现拥堵, 进一步出现丢包的情况 ,当发现丢包后就要减小滑动窗口

反复在 2 -3 步进行循环, 这就构成了一个 " 动态平衡"

通过 拥塞控制机制 , 使得网络发送速度 接近能够承载的最大极限, 既保证传输的速度, 同时能能够减少丢包,还能够适应网络的动态变化.~~

定量分析 :

image-20221109140437343

注意 : 通过流量控制机制 和 拥塞控制机制 ,都能够控制滑动窗口的大小 , 那到底应该取哪一个机制决定的大小呢 ?

应该取两者中较小的那一个 , 取较小值生效

延时应答机制

延时应答也是用来提高效率的机制.

延时应答,则是让滑动窗口能够大一些!

在流量控制机制中 , 通过ACK告知发送方滑动窗口大小是多少适合 , 为了防止流量控制将滑动窗口限制的更狠, 相对的提升一点效率, 引入了延时应答机制.

延时应答机制:

接收方在收到 发送方的数据后, 不立即返回收到的ACK , 而是延时一段时间再发送,

在延时的这段时间内, 应用程序是在不断的从接收缓冲区读取数据的, 接收缓冲区中的数据被读取后, 其剩余空间的大小就变大了,

这时再返回ACK附带的此时接收缓冲区剩余空间的大小给发送方 , 就会使下次发送数据时的 滑动窗口相对未进行延迟应答的窗口更大, 提升一定的发送效率.

image-20221109140310509

上图中 在1-1000 的数据收到后, 进行了延时应答 , 没有立即返回1001的ACK , 而是在1001- 2000的数据到达之后,再进行了应答,应答了2001, ( ACK会涵盖前面的报文, 所以1001相当于也已经应答了)

捎带应答机制

捎带应答也是基于延时应答的一种提升效率的机制.

捎带应答机制

在客户端 和 服务器 交互过程中 , 如果是 一应一答的模式 , 每次客户端的请求都会 得到 服务器的回应 . 而在TCP中, 每次的请求和回应都会给对方返回一个ACK. 这时在延迟应答的基础上 , 如果请求的ACK 在延时后刚好和回应在同一时刻返回 , 二者就可以合成一个数据报进行发送, 提升了效率.

image-20221109140249521

利用这个机制 在四次挥手过程中, 对于主动发送的FIN , 返回的ACK 若是延时应答后 和 接收方发送FIN的时机一样, 也是可以合并的 , 所以四次挥手 此时 可以 变成3次挥手.

粘包问题

TCP协议是面向字节流的 , 只要是面向字节流的传输方式 , 就会产生粘包问题.

粘包问题:

在面向字节流的传输过程中 , 接收方使用一个接收缓冲区,暂时存放接收到的数据, 应用程序再进行读取.

但是因为是 面向字节流传输 , 在接收缓冲区中如果有 多个数据包的数据同时存放 , 这时几个包的数就会粘在一起, 应用程序就无法区分 哪些数据是哪个数据包传输过来的了 , 这就是粘包问题;

解决粘包问题的方案:

在应用层序代码中,明确包之间的边界 :

  1. 使用分隔符
  2. 约定长度
TCP连接异常处理
  1. 主机关机( 正常按程序关机)

    按程序关机, 会直接杀死所有的用户进程 => 释放进程的PCB => 释放文件描述符表对应的文件资源( 调用close 关闭文件) => 触发FIN => 开启四次挥手

    如果在四次挥手过程中 主机已经关机了 , 对端会重传FIN 若干次, 没回应就放弃了

  2. 程序崩溃

    同 主机关机 相同. 因为程序正常关闭 或者 崩溃 都会释放 文件描述符表 相当于调用close关闭文件.

    虽然进程不在了, 但是四次挥手的过程是系统内核负责的, 所以并不影响四次挥手

  3. 主机突然断电

    突然断电, 电脑直接关机 , 肯定来不及进行四次挥手 .

    a) 接收方断电 : 这时候 发送方 ,在发出数据后得不到相应 , 就会触发超时重传机制, 多次重传后还是失败 , 就会尝试重新建立连接 , 仍然失败, 就会认为当前网络出现的问题, 就放弃了

    b) 发送方掉电 : 接收方迟迟等不到要接收的数据 ,这时接收方无法区分,是发送方没发还是发送方出问题了,

    接收方如果一段时间未收到数据 , 就会给 发送方 发送一个**“心跳包”**;

    心跳包 : 接收方发给发送方的 一个特殊报文( ping ) ,如果发送方没问题 就会回应一个特殊报文 ( pong), 说明发送方还没发, 没回复就是 发送方出问题了.

    心跳包是周期性的 ,用于判断对方是否存活.

  4. 网线断开

    同 主机突然断电

TCP 与 UDP的对比
  • 如果对性能要求比较高 , 而对可靠性要求不高, 使用UDP;

  • 对可靠性要求高,优先考虑TCP

  • 如果传输的单个数据比较大,优先考虑TCP

  • 如要进行"广播" ( 直播上课 一对多) , 优先考虑UDP

  • 如果既要求可靠性 , 有要求效率,可使用KCP

    image-20221109140221625

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值