【linux网络(五)】传输层协议详解(下)

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Linux从入门到精通

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学更多操作系统知识

🔝🔝


这Linux网络

  • [1. 前言](#1. 前言)
  • [2. TCP三次握手的过程](#2. TCP三次握手的过程)
  • [3. TCP四次挥手的过程](#3. TCP四次挥手的过程)
  • [4. TCP的滑动窗口机制](#4. TCP的滑动窗口机制)
  • [5. TCP的流量控制机制](#5. TCP的流量控制机制)
  • [6. TCP的拥塞控制机制](#6. TCP的拥塞控制机制)
  • [7. TCP的延迟应答机制](#7. TCP的延迟应答机制)
  • 总结以及思考

1. 前言

强烈建议先看了传输层协议(上)后再看这篇文章. 上一篇文章讲到TCP协议为了保证可靠性而做的一些策略, 这篇文章会深度解析剩下的策略

本章重点:

本篇文章会讲解TCP三次握手和四次挥手的细节过程. 以及TCP协议为了保证可靠性和效率性而做的措施, 比如: 滑动窗口, 流量控制, 拥塞控制, 延迟应答, 捎带应答等. 最后会结合这些措施打通整个套接字编程的调用.


2. TCP三次握手的过程

要明确一点, 三次握手的目的是为了建立连接. 在TCP报头中的六位标记位中, 有一个标记位是SYN. 它代表请求建立连接, TCP三次握手离不开SYN标记位

TCP三次握手的过程可以总结为: 客户端向服务器发送一个包含SYN标记位的报文. 服务器收到此报文后, 向客户端发送一个包含了ACK和SYN标记的报文, 标识着服务器收到了客户端的SYN, 并且服务器也想和客户端建立连接. 客户端收到此报文后, 会再给服务端发送一个ACK, 一旦发送此ACK后, 客户端就已经建立好了连接. 同理, 服务器收到此ACK后也就建立好的连接

可以在途中看到, 服务器在listen状态时会收到客户端的SYN报文, 对应在代码中也就是使用listen函数进行监听. TCP三次握手建立连接的过程和accept函数是没有关系的, accept函数负责当连接建立好后, 将连接拿到内存当中!

建立一个常识:

为什么是三次握手?其它次数行不行?

这个问题分为奇数次握手和偶数次握手来讲解:

1. 偶数次握手

拿四次握手为例, 只要是奇数次握手, 那么最后一个ACK报文一定是服务器发送给客户端的. 此时就会有一个问题: 服务器发送了ACK之后, 就认为建立好连接了. 但是我们知道, 维护连接是有成本的, 如果我们发送回去的ACK报文, 客户端不进行处理, 但是我们又为它建立了连接, 这不是浪费资料了吗? 是的, 不仅如此, 如果使用偶数次握手, 客户端可以无脑向服务器发送SYN报文, 并且收到服务器的ACK报文后不建立连接. 这样就可以低成本的攻击一台服务器, 让它维护的连接数过大, 从而把这个服务挂掉. 而奇数次握手, 一定是客户端最后一个发送ACK, 一旦客户端发送了ACK后就会进行连接状态, 服务器会收到ACK后才会建立连接, 可以防止别人无成本攻击你的主机

2. 奇数次握手

很明显奇数次握手肯定是比偶数次握手好的, 那么现在来分析一下, 为什么奇数次握手是三次, 不是一次也不是五次? 很明显一次握手是无法完成建立连接这个任务的. 那么五次握手行不行呢? 当然是可以的, 但是三次握手就足够建立连接, 五次, 七次也能建立连接, 但是三次是最优解!


3. TCP四次挥手的过程

四次挥手的目的是为了断开连接. 在TCP的六位标记位中有一个叫FIN的标记位, 它代表要和对端断开连接. 四次挥手离不开这个标记位

客户端会先给服务器发送一个带有FIN标志位的报文, 服务器收到此请求后会给客户端返回一个ACK, 并且会将没发完的数据发给客户端. 发完后会给客户端发送FIN,标识我的数据已经给你发完, 可以关闭连接了. 此时客户端收到此消息后给服务器发送一个ACK就断开连接了.

对TIME_WAIT状态的理解

连接断开后, 会维持一段时间的TIME_WAIT状态, 在此期间, 不能重新在同样的端口启动服务


为什么是TIME_WAIT的时间是2MSL?

若C端发送FIN时, s端恰好也想和c端断开连接, 那么s端可以把FIN和ACK一起发送给c端. 所以四次挥手在某种情况下也是三次挥手


4. TCP的滑动窗口机制

上篇文章中讲到, 服务器每收到一次数据都会回一个ACK确认应答. 但是这样是不是效率有点低下了?由其是数据往返时间比较长的时候. 有没有什么方法可以一次性发送多条数据呢? 当然TCP协议也考虑到了这一点:

滑动窗口:

可以将发送缓冲区想象成一个数组, 滑动窗口的本质就是此数组中的两个元素下标, 这两个下标会维护一段数据, 我们就可以将这份维护的数据一次性发给对方, 能大大的提高效率

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

滑动窗口中的这两个指针, 会随着对端发过来的确认序号进行左右移动. 还有一点: 上一篇文章讲过TCP的超时重发机制, 你有没有想过, 重发这个数据时, 这个数据在哪里? 没错, 它肯定是在我们的滑动窗口里. 因为没有收到对端的ACK, 所以肯定也没收到对端发来的确认序号, 只要收不到确认序号, 滑动窗口就不会向右滑动(不会丢失数据). 滑动窗口的本质其实就是sender一方,可以一次性向对方推送数据的上限


5. TCP的流量控制机制

这个很好理解, 上篇文章中说到16位窗口大小代表对端接受缓冲区的剩余空间. 所以流量控制也就是基于对方的窗口大小来设计的

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应. 因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)

问题:第一次发数据时怎样知道对方接受能力?

答案: 其实早在TCP三次握手时, cs双方就会交换各自的接受缓冲区的情况


6. TCP的拥塞控制机制

虽然是TCP有滑动窗口这一大杀器来提高效率和可靠性, 但是我们始终在关心的是, 对端怎么怎么样, 对端的接受能力怎么样, 对端是否收到了信息. 我们从来没有考虑过网络本身的情况, 万一网络本身就不好, 还一次性发送了大量数据, 丢包了怎么办? . 当然这一切都在TCP协议的预料之中

拥塞控制:

因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的. TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;

其实你只用知道, TCP的拥塞控制是用来关心当前网络状态的, 并且它采用的慢启动机制, 虽然这其中还有很多细节可以挖掘, 但是作为从事后端开发的大学生而言, 学到这个地方已经是有点东西了


7. TCP的延迟应答机制

若接收数据后立刻返回ACK应答, 返回窗口会比较小.

  • 假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;
  • 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
  • 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;
  • 如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;

一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率;

说白了, 有时候立刻应答并不是一种高效率的体现


总结以及思考

现在再来看, TCP是面向字节流的就大概能理解了. TCP只管迅速的给对端发包, 不管一个数据要分几次发, 一次性发多少, 总之它只管尽快将包发过去. 虽然这样大大的提升了效率, 但是会带来粘包问题. 也就是说对端收到数据后, 它并不知道这个数据是否是完整的一个, 还是半个, 甚至是两个半都有可能.

TCP小结:

TCP协议很复杂, 它有许多机制来保证可靠性. 但是它的效率一定就比UDP慢吗? 那还真不一定, 虽然是UDP协议简洁, 但是TCP协议有机制来提高效率. 所以说谁快谁慢还真不一定


🔎 下期预告:IP协议详解 🔍

相关推荐
新手嵌入式学习2 分钟前
网络协议头分析
网络·网络协议
小安运维日记8 分钟前
Linux云计算 |【第三阶段】PROJECT1-DAY1
linux·运维·云计算·apache
pyliumy25 分钟前
rsync 全网备份
linux·运维·服务器
苹果醋31 小时前
SpringCloud系列之一---搭建高可用的Eureka注册中心
运维·nginx
诚诚k1 小时前
docker存储
运维·docker·容器
sorel_ferris1 小时前
Ubuntu-24.04中Docker-Desktop无法启动
linux·ubuntu·docker
ggb19991 小时前
【python的坑】vpn下,python request报错 check_hostname requires server_hostname
linux·运维·服务器
小O_好好学1 小时前
vi | vim基本使用
linux·编辑器·vim
-SGlow-1 小时前
Linux相关概念和重要知识点(4)(自举、vim)
linux·运维·vim
运维Z叔2 小时前
云安全 | AWS S3存储桶安全设计缺陷分析
android·网络·网络协议·tcp/ip·安全·云计算·aws