🌈欢迎来到Linux专栏 ~~ 网络编程
- 🌍博客主页 :张小姐的猫~江湖背景
- 🔥所属专栏 :Linux ~ 不破不立
- 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

网络编程

假设对方的接受能力是4000字节的话,那么我发送的时候,为什么直接一次发送4000字节呢?反而是发送了多个报文??
- 答案在
Mac帧处再讲解,主要原因是在数据链路层发送的数据不能太大,有长度限制
⚙️拥塞控制
之前我们了解到的策略都只是和发送端和接收端有关的! 但是这个报文会经过网络的,那不是就要考虑网络中的情况吗??
TCP为了保证可靠性,它不仅仅考虑了发送端和接收端的策略,还考虑了网络情况
一旦我们在通信时,出现了大规模的丢包问题,就判定是网络出现问题了 ------ 出现了网络拥塞
- 丢一个两个报文,我们就进行重传!没问题✅️
- 那么如果是500个报文,丢了495个,还需要进行重传吗?? ❌️不能立即进行重传!因为会加重网络的拥堵情况

像上图,如果出现了网络拥堵,还对丢包的报文大面积进行重传 ------ 网络里继续涌入大量的重传报文 ------ 加重网络拥堵
- 不应该是大家都暂停一会,让我把拥堵情况先消化一下,再进行重传
🏝️拥塞控制
再进行下面改进时的前提是:网络已经拥塞了
TCP引入慢启动机制,先发少量的数据探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据
- 发送一个,收到后;再发送两个也收到后、才发送四个...

此处引入一个新概念:拥塞窗口 :是 TCP 发送方根据网络拥塞状况动态控制的一个窗口大小,用来限制发送方在收到 ACK 之前 ,最多可以向网络中发送多少数据
- 发送数据超过该窗口,可能会发送网络拥堵 ------ 衡量网络拥塞的指标(网络的接收能力)
到现在我们认识了三个窗口
- win窗口 (报头窗口大小字段)
- 滑动窗口
- 拥塞窗口
这三个窗口的关系是什么呢??
滑动窗口大小 = min(win窗口 ,拥塞窗口)
win窗口防止"把接收方撑爆",拥塞窗口防止"把网络撑爆",TCP实际能够发送的数据量取两者的较小值


1️⃣是怎么做到发送方一旦识别到网络拥塞,就可以灵活的控制它发送报文的数目呢?
- 本质是通过拥塞窗口来限制滑动窗口大小
- 拥塞窗口设置为1000,即便你接收能力再大,滑动窗口也只是1000;不断的增加拥塞窗口即可
但是真实情况下,拥塞窗口是不可能一直指数的增长的!因为要考虑对端的接受能力!
那怎么理解慢启动呢?
- 前期增长比较慢,
1 - 2 - 4 -....,后期恢复特别快 - 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。
- 此处引入一个叫做慢启动的阈值
- 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性式增长

当TCP开始启动的时候,慢启动阈值等于窗口最大值
在每次超时重发的时候,慢启动阈值会变成原来的⼀半,同时拥塞窗口置回1
少量的丢包(丢包率1% ~ 1.5%),我们仅仅是触发超时重传;大量的丢包(3%以上),我们就认为网络拥塞;
当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案.
TCP拥塞控制这样的过程,就好像热恋的感觉
拥塞窗口大小一定是变化的!因为网络是变化的! 发送拥堵的上限一定是变化的
细节:那TCP自己是怎么知道,这个拥塞窗口大小一个是多少呢?
- TCP也不知道,只能通过不断的试才知道
- 慢启动 的本质:探索当前网络的接收能力!
🐴延迟应答
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小

延迟应答的本质:通过延时,一定概率可以给发送方通告一个更大的接收窗口

⼀定要记得,窗口越大。网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
- 那么如果拥塞窗口比较小呢?不一样发不了很多数据 ------ 和我没关系了啊,我已经尽量给到一个大的接收窗口了,网络拥塞是网络的事情了
那么所有的包都可以延迟应答么?肯定也不是
- 数量限制 :每隔
N个包就应答⼀次; - 时间限制:超过最大延迟时间就应答⼀次;
具体的数量和超时时间,依操作系统不同也有差异;在Linux里⼀般N取2,超时时间取200ms
♻️捎带应答
在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是"⼀发⼀收"的。意味着客户端给服务器说了"Howareyou",服务器也会给客户端回⼀个"Fine,thankyou"
那么这个时候ACK就可以搭顺风车,和服务器回应的"Fine,thankyou"⼀起回给客户端
TCP地位是对等的,我可以给你发,你也可以给我发

既然我要给你做应答 + 发数据,为什么不整合在一起呢?于是有了捎带应答:序号是自己的,确认序号是对收到数据 + 1
三次握手里的第三次的ACK可以带数据吗??
- 对于发送方,第三次ACK是可以带数据的,因为ack发出发送方就认为三次握手就完成了
- 如果这个ack没丢,那么接收方就可以拿到对应发送方发来的数据了 ------ 也叫 捎带应答
🆚TCP小节
为什么TCP这么复杂?因为要保证可靠性 ,同时又尽可能的提高性能
可靠性:
- 校验和 ------ 校验失败,报文丢弃 再进行重传
- 序列号(按序到达、去重)
- 确认应答 ------ 对历史报文是
100%可靠的 - 超时重发、快重传
- 连接管理 ------ 三次握手、四次挥手
- 流量控制(滑动窗口)
- 拥塞控制
提高性能:
- 滑动窗口 ------ 让多个
TCP数据段可以同时发送 - 快速重传
- 延迟应答
- 捎带应答
✅️常考面试题
🌊面向字节流
创建⼀个TCP的socket,同时在内核中创建⼀个发送缓冲区 和⼀个接收缓冲区
- 调用
write时,数据会先写入发送缓冲区中 - 如果发送的字节数太长,会被拆分成多个TCP的数据包发出
- 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时间发送出去
- 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区
- 然后应⽤程序可以调⽤read从接收缓冲区拿数据
- 另⼀方面,TCP的⼀个连接,既有发送缓冲区,也有接收缓冲区,那么对于这⼀个连接,既可以读数据,也可以写数据。这个概念叫做全双工**
由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:
- 写100个字节数据时,可以调用一次
write写100个字节,也可以调用100次write,每次写⼀个字节 - 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以⼀次read100个字节,也可以⼀次read⼀个字节,重复100次;
TCP 不关心你发送的是"一条消息"还是"一个文件",它只把数据看成一串连续的字节(byte),负责可靠、有序地把这串字节交给对方
🍔粘包问题
因为tcp是面向字节流的,所以会存在粘包问题!

那么如何避免粘包问题呢?归根结底就是⼀句话,明确两个包之间的边界
❌️TCP异常情况
进程终止 :进程终止会释放文件描述符,仍然可以发送FIN。和正常关闭没有什么区别

机器重启 :和进程终止的情况相同(重启不也是 要对进程关闭嘛)
机器掉电/网线断开 :网线被拔了后(发送端想告诉对方都不行了)即使没有写入操作,TCP自己也内置了⼀个保活定时器,会定期询问对方是否还在。如果对方不在,也会把连接释放
- 接收端本地
socket状态还认为连接ESTABLISHED(连接存活),但对端早就已经断开连接了;当本机调用write往这个 "假活着" 的连接发数据的时候,内核发现实际连接已经失效,直接发出RST复位报文,把连接强制销毁 - 如果拔的是服务器的网线呢? ------ 客户端认为链接还在,就会向对方发送数据 ,服务器端却不记得了,我们之间没建立链接,怎么就给我直接发数据了,服务器就会给发送端发送一个应答(其中
RST置为1)要求链接进行重置
另外,应用层 的某些协议,也有⼀些这样的保活检测机制 。例如HTTP长连接中,也会定期检测对方的状态。例如QQ,在QQ断线之后,也会定期尝试重新连接.
📢写在最后
接下来登场的是 网络层 IP

