文章目录
1.概述
传输层是网络中最重要的部分
⑴TCP和UDP的区别
- TCP是建立连接的,UDP是无连接的,建立连接,指的是在客户端和服务端维护连接
- TCP无差错、不丢失、不重复、并且按序到达,而UDP完全继承了IP包的特性,没有保证。
- TCP是面向字节流的,发送的时候发的是一个没头没尾的流,而UDP继承了IP包,基于数据报的,一个个发,一个个收。
- TCP具有拥塞控制等特性,UDP只管发发发
2.UDP
⑴UDP结构
UDP的包头除了源端口号和目标端口号就没别的了
⑵UDP的使用场景:
- 丢包不敏感的应用
- 不面向连接的功能,可以使得可以承载广播或者多播的协议。例如 DHCP、VXLAN、QUIC 等。
- 需要处理速度快,时延低,可以容忍少数丢包
3.TCP
⑴TCP的包头格式
- 源端口号和目标端口号
- 序号,解决乱序问题
- 确认序号,解决不丢包问题
- 状态位
- SYN 发起一个连接
- ACK 回复
- RST 重新连接
- FIN 结束连接
- 窗口大小 流量控制
⑵三次握手
-
三次握手过程
- 一开始A(客户端)和B(服务端)处于CLOSED 状态
- B开始主动监听某个端口,处于 LISTEN 状态
- A发起SYN,之后处于SYN-SENT状态
- B收到后返回SYN,并且ACK A的SYN,处于SYN-RCVD状态
- 然后A再对B的ACK进行ACK,之后处于ESTABLISHED状态
- B收到A的ACK之后,处理ESTABLISHED状态
-
为什么不是两次握手?
A 和 B 原来建立了连接,做了简单通信后,结束了连接。A 建立连接的时候,请求包重复发了几次,有的请求包绕了一大圈又回来了,B 会认为这也是一个正常的的请求的话,因此建立了连接,然后B就结束不了了。 -
三次握手沟通TCP包的序号问题
每个连接都要有不同的序号。这个序号的起始序号是随着时间变化的
⑶四次挥手
-
四次挥手过程
- A发送关闭信息,进入FIN_WAIT_1状态
- B收到后回复A,进入CLOSE_WAIT状态
- A收到B的回复,就进入 FIN_WAIT_2 的状态
- 然后B发送关闭信息,A回复后从从 FIN_WAIT_2 状态结束
- A 等待2MSL的时间,防止B没有收到ACK然后重新向A发送关闭信息,保证A也有足够的时间ACK
- 假如超过2MSL的时间B还没有收到ACK而继续向A发送,A如果收到了,就会直接向B发送RST,B也就知道A已经跑了。
-
MSL
报文最大生存时间(Maximum Segment Lifetime) -
为什么要四次挥手
我感觉是为了保证B在CLOSE_WAIT状态处理完该处理的事情。
A向B发送10这个ACK后,B就可以直接停止了,但是A为什么需要等待2MSL呢?
- 情况1:
如果B没有收到ACK,就会重新发9,这个时间正好是2MSL
- 情况2:
比如A和B过了一会又勾搭上了,为了方式老的TCP报文在网络中溜达一圈又来了,所以还得等待2MSL,这2MSL正好是发送和应答的和
⑷TCP状态机
在这个图中,加黑加粗的部分,是上面说到的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户端 A 的状态变迁,加粗的虚线是服务端 B 的状态变迁。
⑸累计确认
TCP 协议每一个包都有一个 ID,建立连接的 时候,会商定起始的 ID 是什么,然后按照 ID 一个个发送,接收端不会一个一个的应答,而是说某个 ID之前都已收到。
⑹缓存
- 发送方让自己的发送窗口最大值 = min(滑动窗口,拥塞窗口)
①发送端缓存
发送端的缓存里是按照包的 ID 一个个排列,分成四个部分
- 发送了并且已经确认的
- 发送了并且尚未确认的,如果没有收到 ACK,就重发几次,直到报错
- 没有发送,但是已经等待发送的
- 没有发送,并且暂时还不会发送的
②接收端缓存
- 接受并且确认过的
- 还没接收,但是马上就能接收的
- 还没接收,也没法接收的
⑺滑动窗口(rwnd)
顺序问题、丢包问题、流量控制都是通过滑动窗口解决。
在 TCP 里接收端会给发送端报一个窗口的大小,大小等于发送端缓存的第二部分加上第三部分。
1、2、3 已经发送并确认;4、5、6、7、8、9 都是发送了 还没确认;10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的
1、2、3、4、5 是已经完成 ACK,但是没读取的;6、7 是等待接收的; 8、9 是已经接收,但是没有 ACK 的。
发送端和接收端当前的状态如下:
- 1、2、3 没有问题,双方达成了一致。
- 4、5 接收方说 ACK 了,但是发送方还没收到,有可能丢了,有可能在路上。
- 6、7、8、9 肯定都发了,但是 8、9 已经到了,但是 6、7 没到,出现了乱序,缓存着但是没办法 ACK。
①丢包问题与顺序问题
- 超时重传
每一个发送了,但是没有 ACK 的包,都有设一个定时器, 超过了一定的时间,就重新尝试,这个时间会不断变化(重传一次时间加倍)。问题是超时周期可能相对较长。 - 快速重传
当接收方收到一个序号大于下一个所期望的报文段时,就检测 到了数据流中的一个间格,于是发送三个冗余的 ACK,客户端收到后,就在定时器过期之 前,重传丢失的报文段。
比如接收方发现 6、8、9 都已经接收了,就是 7 没来,那肯定是丢了,于是发送三个 6 的 ACK,要求下一个是 7。客户端收到 3 个,就会发现 7 的确又丢了,不等超时,马上重发。 - SACK
这种方式需要在 TCP 头里加一个 SACK 的东西,可以将缓存的地图发送给发送方。例如可以发送 ACK6、SACK8、 SACK9,因此发送方一下子就能看出来是 7 丢了。SACK只是针对失序到达的报文段的。
②流量控制
滑动窗口大小是可以变化的。
接收端的应用一直不读取缓存中的数据,那么接收已确认的部分会增大,等待接收未确认的会变小。从而也影响了发送端窗口变小。
比如我们6接收确认但是没有处理,窗口大小就会减一
同样,发送端窗口也会减一
⑻拥塞窗口(cwnd)
拥塞控制是通过拥塞窗口来解决。拥塞窗口 cwnd,是怕把网络塞满。拥塞窗口和 滑动窗口共同控制发送的速度:
LastByteSent - LastByteAcked <= min {cwnd, rwnd}
拥塞控制就是在不堵塞,不丢包的情况下,尽量发挥带宽。
①TCP慢启动、拥塞避免、快速重传,拥塞检测
- 慢启动(指数级增长)
- 拥塞避免(线性增加)
TCP使用了一个叫慢启动门限(ssthresh,一般是65536字节)的变量,当cwnd超过该值后,慢启动过程结束,进入拥塞避免阶段 - 快速重传
当接收端发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速的重传,不必等待超时再重传。TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,cwnd 减半为 cwnd/2,然后 sshthresh = cwnd,当三
个包返回的时候,cwnd = sshthresh + 3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长。
②TCP BBR 拥塞算法
就是通过不断的加快发送速度,将管道填满,但是不要填满中间设备的缓存
4.Nagle算法
通俗点说Nagle算法就是服务端将多个请求合并成一个,发给客户端,为了避免延迟过大,有一个定时器,定时器时间到了,立马将缓冲区的数据发送出去。
Nagle算法是时代的产物,因为当时网络带宽有限。而当前的局域网、广域网的带宽则宽裕得多,目前的TCP/IP协议栈默认将Nagle算法关闭,即通过SO_NODELAY = 1