TCP-UDP详解
1. UDP详解
1.1 什么是UDP?
UDP(User Data Protocol,用户数据报协议):属于传输层的协议,无连接,UDP适用于一次传输少量数据,对可靠性要求不高的应用环境。
- 不可靠(不保证不丢失,不保证按顺序到达)
- 基于数据报(一个一个地发,一个一个地收)
- 无状态(发出去就完事了,啥都不管)
- 无控制(不会根据外部环境(网络环境)来进行发包控制,应用让发就发,什么都不考虑)
1.2 UDP包头分析
- 源端口号和目的端口号:通信双方的端口号
- UDP长度:包头+数据
- 校验和:基于某种算法,用来检验数据合法性
2. TCP详解
2.1 什么是TCP?
TCP(Transmission Control Ptotocol,传输控制协议),面向连接,发送数据之前双方必须建立连接(三次握手)。
- 可靠
- 基于字节流(无头无尾)
- 有状态
- 有控制
2.2 TCP包头
- 源端口号和目的端口号:通信双方的端口号
- 序号:为了解决乱序问题,确定包的先后顺序。
- 确认序号:确定包是否被收到,没有收到就需要重发。解决丢包问题。
- 首部长度(数据偏移):指出TCP报文段的数据起始位置距离报文段的起始位置有多远。
- 保留:保留为后面使用
- 6个控制位:
URG(紧急):当URG=1时,表明紧急指针有效。告诉系统存在紧急数据,应该优先发送,不需要排队。
ACK(确认):ACK=1时有效。TCP规定,在连接建立后所有的传送报文段都必须将ACK置为1.
PSH(推送):一端的应用进程希望在键入一个命令后立即就能收到对方的响应。将PSH置为1,这是接收方收到PSH=1的报文段,就立刻交付给应用进程,免排队。
RST(复位):当RST=1时,表示TCP连接中出现了严重错误,需要立即释放连接,然后重新建立连接。
SYN(同步):在建立连接时同步序号。SYN=1,ACK=0是一个请求报文;SYN=1,ACK=1是请求确认报文。
FIN(终止):是来释放一个连接。当FIN=1时,表明此报文段的发送的数据完毕,并要求立即释放连接。
- 窗口大小:[0-2^16-1]之前的整数,窗口大小明确指出现在允许对方发送的数据量大小,TCP用这个来进行拥塞控制等操作。
- 校验和:用来校验数据的合法性,校验范围包括首部和数据。
- 紧急指针:紧急指针只在URG=1时有效,它指出本报文段紧急数据的字节数(紧急数据结束后就是普通数据)。这里需要注意的是,即使窗口大小为0也可以发送紧急数据。
- 选项:长度可变,最大32位
最大报文段长度MSS:每一个报文段中的数据字段的最大长度。
窗口扩大选项:为了扩大窗口。新的窗口值就是16+S,S(移位值)最大值是14。窗口扩大选项可以在双方初始建立TCP连接是进行协商。如果连接的某一端实现了窗口扩大,当不需要时,可发送S=0的选项,使窗口大小回到16。
时间戳选项:
最主要的字段就是时间戳字段和时间戳回送回答字段。
1.用来计算往返时间。
2.用于处理TCP序号超过2^32的情况,这有称为防止序号绕回PAWS。
2.3 TCP三次握手
三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是TCP包的序号问题。
2.4 TCP四次挥手
2.5 如何实现可靠性传输
2.5.1 累计确认/累计应答
为了保证顺序性,每个包都有一个对应的序号,在建立连接的时候会商定起始序号是多少,然后按照序号一个一个地发送。为了保证不丢包,对于发送的包都要进行应答,但是这个应答也不是一个一个来的,而是会应答某个之前的序号,表示都收到了。
为了记录所有发送的包和接收的包,TCP也需要发送端和接收端分别都有缓存来保存这些记录。
发送端的缓存里是按照包的序号一个个排列的,根据处理的情况分为四个部分。
接收端缓存:
2.5.2 顺序问题与丢包问题
确认与重发机制
- 超时重试:对于每一个发送后没有收到ACK的包,都设有一个定时器,超过一定时间就重新尝试。时间的确定采用自适应重传算法。
- 超时间隔加倍:每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值得两倍。
- 快速重传:当接收方收到一个序号大于下一个期望的报文段时,就检测到了数据流中的一个间隔,于是发送几个冗余的ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。
2.5.3 流量控制问题(发送方把接收方缓存塞满)
rwnd:滑动窗口
当发送方发送数据过快或者其他什么原因导致接收方处理不过来现有的请求时,发送方可以通过调整窗口大小来告诉发送方减缓发送甚至是暂停发送。
当发送方停止发送时,发送方还是会定时发送窗口探测数据包,看是否有机会调整窗口大小。当接收方处理比较慢的时候,要防止低能窗口综合症,不要刚空出一个字节就赶快告诉发送方,这时候就会立马填满,可以等到空出一半的缓冲区再去通知发送方更新窗口。
2.5.4 拥塞控制问题(网络塞满)
cwnd:拥塞窗口
TCP的拥塞控制主要是来避免两种现象,包丢失和超时重传。
一条TCP连接开始,cwnd设置为一个报文段,一次只能发送一个;当收到确认的时候cwnd加一,于是一次能发送两个;当收到这两个的确认时cwnd加二,于是能发送四个;收到四个确认的时cwnd加四,于是一次能发送八个。指数级增长。什么时候停止增长呢?TCP里面有一个值ssthresh为65535个字节,当超过这个值的时候,就需要降低速度了,每当收到一个确认后,cwnd增加1/cwnd,一次发送八个,当着八个的确认到来的时候,每个确认增加1/8。八个一共增加1,于是一次发送九个,变成了线性增长。
但是这种情况还是在持续增长,水满则溢会造成丢包,这个时候将ssthresh设为cwnd/2,将cwnd设为1,重新开始慢启动。但是这种方式又会造成网络卡顿。
前面我们讲过快速重传算法。当接收端发现丢了一个中间包的时候,发送三次前一个包的ACK,于是发送端就会快速的重传,不必等待超时重传。TCP认为这种情况不严重,因为大部分没有丢失,cwnd减为cwnd/2,然后ssthresh=cwnd,当三个包返回的时候,cwnd=ssthresh+3,也就是还在比较高的值,继续呈线性增长。
这样的处理方式,在时延很重要的时候反而降低了速度。
两个现象:
- 丢包并不代表通道满了,也许是通道本来就漏水
- TCP的拥塞控制要等到将中间设备的缓存都填满了才发生丢包,从而降低速度,这时候其实已经晚了。
为了优化这两个问题,后来有了TCP BBR拥塞算法。它试图找到一个平衡点,就是通过不断的加快速度,将管道填满,但是不要填满中间设备的缓存,因为这时候时延会增加,在这个平衡点可以很好的达到高带宽低时延的平衡点。