发表于

TCP & UDP

Authors

TCP & UDP

1. UDP

UDP协议说到底是面向无连接,能不能送达没关系 最多不过丢包罢了

每一个UDP应用对应一个端口,源端口 目标端口的信息在数据包里面。一般来说UDP 受到 运营商的 QoS(Quality of Service) 影响,当UDP丢包之后 不会发生重传 所以就会看到很多游戏中 有人会瞬移(笑),这就是游戏客户端与Server层的连接用的是UDP协议传输然后因为某些情况丢包的关系。

2. TCP

TCP和UDP不一样,TCP协议需要保证数据包的传输的一致性和顺序性。为了做到防止丢包,发送方发出的消息,需要知道对端是否收到了。发送方给每个发出的包都编排一个序号X, 若是对端收到了序号为X的包,则要告知对方序号为X的包收到了。 因为TCP还要保证包到达的顺序,告知序号为X的包收到了,则意味着序号X之前的包也都收到了。此时发送方等待一定时间之后未收到确认消息,会触发重传。

TCP的机制是以下几点:

  • TCP包按照seq递增编排
  • ack已经顺序收到的包
  • 保证包到达的顺序,如果包通过网络到达的顺序不一样,对端也会等待序号较小的包到达后再交付应用层。
  • 丢包重传,超时未收到则使用超时重传机制
  • 减少丢包
    • 告知接收窗口:感受对端的处理能力
    • 丢包的原因以及优化:感受网络的拥塞,控制传输的速率

用户浏览网页时感受到网络速度慢的解决方案就是尽可能减少丢包以及当数据包丢失的时尽快触发重传

产生丢包的原因有很多:有可能是网络窗口大小不够 有可能是数据包传输过程中丢包

网络窗口不足的情况 需要通过算法协商来实时计算滑动窗口

这里主要是试探并确认了:

确认A发送数据的能力:A协商了自己的初始序列号和接收窗口大小 确认B接收数据的能力,B确认收到了A的第一条序列号的消息 确认B发送数据的能力,B协商了自己的初始序列号和接收窗口大小 确认A接收数据的能力,A确认收到了A的第一条序列号的消息

此外,这个试探过程也叫做三次握手,我们说,通过三次握手两个应用程序之间已经相互了解,建立了一条TCP连接。

三次握手也就是三次发包的过程:

  1. 发起端发送SYNC包:SYN,seq=x,自己进入Sync-Sent状态
  2. 监听端收到SYNC包,发送SYN,ACK,seq=y,ack=x+1,进入Sync-RCVD状态
  3. 发起端收到SYNC,ACK包,发送ACK,seq=x+1,ack=y+1,进入Established状态
  4. 监听端收到ACK包,进入Established状态

2.1. TCP报文

从这个报文格式和上面三次握手的过程可以看出:

  • SYN标记位为1说明这个报文是一个SYN类型的包,用于握手
    • 发起端发送SYN包:SYN,seq=x
    • 监听端收到SYN包后,也发送自己的SYN包:SYN,seq=y
    • 发起端和监听端的起始序号x和y是32位序号,它们是系统随机生成的
    • 在SYN报文中交换了初始序列号之后,这个序列号就一直单调递增
  • ACK标记位为1说明这个报文是一个ACK类型的包
    • 32位确认号
    • 监听端收到SYN包,还发送ACK,ack=x+1表示小于x+1的全部字节已经收到,期待下一次收到seq=x+1的包
    • 发起端收到SYN,发送ACK,ack=y+1表示小于y+1的全部字节已经收到,期待下一次收到seq=y+1的包
  • 用于SYN连接的包的数据为空
  • ACK可以和其他数据包组合在一起,通过一个包发送,比如第二次握手的AYN+ACK包就是

另外报文格式中还包含下面信息:

  • 接收到的SYN包的窗口大小代表对方的接收窗口的大小
  • RST标记位为1: 可以用于强制断开连接
  • PSH标记位为1: 告知对方这些数据包收到后应该马上交给上层的应用
  • 选项中的MSS:TCP允许的从对方接收的最大报文段,这个和链路层的MTU大小有关

2.2. 超时重传&拥塞控制

当发送端在一定时间之后未收到ack数据包,即触发超时重传,在超时重传过程中,若ack的值大于当前传输包的值并且在超时重传等待时间内,即认为前面的包全部被收到,不触发重传

拥塞控制算法就是为了解决何时超时重传的一种算法,通过一定的算法来实时计算并修改滑动窗口的大小从而能更多的数据包一次发送出去。

拥塞控制的原则是,只要网络中没有出现拥塞,拥塞窗口的值就可以再增大一些,以便把更多的数据包发送出去,但只要网络出现拥塞,拥塞窗口的值就应该减小一些,以减少注入到网络中的数据包数。

TCP 拥塞控制算法发展的过程中出现了如下几种不同的思路:

  • 基于丢包的拥塞控制:将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大拥塞窗口,当出现丢包时,将拥塞窗口减小,如 Reno、Cubic 等。
  • 基于时延的拥塞控制:将时延增加视为出现拥塞,延时增加时增大拥塞窗口,延时减小时减小拥塞窗口,如 Vegas、FastTCP 等。
  • 基于链路容量的拥塞控制:实时测量网络带宽和时延,认为网络上报文总量大于带宽时延乘积时出现了拥塞,如 BBR。
  • 基于学习的拥塞控制:没有特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习的方法形成一个控制策略,如 Remy。
国外VPS使用一般来说使用BBR或者其他一些基于BBR修改的算法,但是在Linux Kernel 5.4之后,BBR采用了BBRPlus中修改的部分代码,因此在现有的Mainline的Linux Kernel下,使用BBR算法可能比其他改动的算法更好,若采用Xanmod Kernel,还可采用BBRv2 算法,但是由于BBRv2算法尚未完全开发完成,因此其对于网络的优化效果尚不得知

2.3. 滑动窗口&TCP顺序保证

2.4. 长连接&短连接

长连接短连接
Client与server完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
所以一条连接保持几天、几个月、几年
者更长时间都有可能,只要不出现异常情况或由用户(应用层)主动关闭。
长连接可以省去较多的TCP建立和关闭的操作,减少网络阻塞的影响,减少CPU及内存的使用,因为不需要经常的建立及关闭连接。
连接数过多时,影响服务端的性能和并发数量。
一般只会在 client/server间传递一次请求操作
这时候双方任意都可以发起close操作
短连接管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段。
通常浏览器访问服务器的时候一般就是短连接。

长连接一般用于数据库连接 或者 对于性能要求较高的 短连接一般对于并发量大,请求频率低的使用

2.5. TCP连接关闭

  • 关闭连接发起方 发起第一个FIN,处于FIN-WAIT1
  • 关闭连接被动方的内核代码回复ACK,此时还可以发送数据,处于Close-WAIT状态
    • 关闭连接被动方等待应用程序发送FIN,如果上层应用一直不发FIN,就还可以继续发送数据
  • 关闭连接发起方收到ACK后处于FIN-WAIT2状态,还可以接收数据自己不再发送数据
  • 关闭连接被动方直到应用程序发出FIN,处于LAST-ACK状态
  • 关闭连接发起方收到FIN,会发送ACK,自己会处于TIME-WAIT状态,此时
    • 若是关闭连接被动方收到ack,就close连接
    • 若是是关闭连接被动方没收到ack,则会重传FIN

Time wait 最多两个报文最大生存时间