TCP和UDP的区别
首先TCP是面向连接的,UDP是无需连接的,TCP有着三握四挥,并且三次握手和四次挥手是对TCP建立的连接有着重要意义的两步,并且TCP是对IP无可靠性提供可靠性的源头,UDP继承了IP的特性,不保证不丢失包,不保证按顺序到达
TCP面向字节流,发送的时候是一个流,没有头尾,IP包不是一个流,而是一个个的IP包,UDP也是如此
TCP是有拥塞控制的,但是UDP没有
MAC层去掉之后,IP层首部会有一个8位的协议,这里会存放着数据里到底是TCP还是UDP,当然这里是UDP,如果我们知道UDP格式就可以解析出来了
下一步就通过UDP包中的目标端口号,将这个包交给应用程序处理
源端口和目标端口不可少,包的序号是为了解决乱序问题,为了解决包的先后顺序,还有就是确认序号,发出去的包要有确认,不然无法知道是否收到,若没有收到就要重新发送,直到送达,这就是TCP的不丢包的实质
对于TCP来说,IP层丢不丢包管不着,但是在TCP层,会努力保证可靠性
一开始,客户端和服务端都处于CLOSED状态,先是服务端主动监听某个端口,处于LISTEN状态,然后客户端主动发起连接SYN,之后处于SYN-SENT状态,服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态。客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了,服务端收到ACK的ACK之后,处于ESTABLISHED,因为它也一发一收了
所以三次握手就能确认双发收发功能都正常,缺一不可。
最后客户端A的TIME-WAIT状态时间要足够长,长到如果B没有收到ACK的话,B会再次发送FIN关闭连接,A会重新发送一个ACK并且时间足够长到这个包到B
A如果直接跑路的话,它的端口就空出来了,但是B不知道,原来发的包如果在路上,但是这时突然另一个应用开启在了这个端口上,那不就混乱了,所以A也需要等待足够时间,等到B发送的包在网络中挂掉之后再空出端口来
等待时间设置为2MSL,报文最大的生存时间,协议规定MSL为2分钟,实际应用中常用的是30s,1分钟和2分钟等
为了记录所有发送的包和接收的包,TCP也需要发送端和接收端分别都有缓存来保存这些记录,发送端的缓存里是按照包的ID一个个排列,根据处理情况分为下面四个部分
在TCP里,接收端会给发送端报一个窗口的大小,叫做Advertised window,这个 窗口大小 应该等于上面说的第二部分加上第三部分也就是 已经发送了但是没有得到确认的加上还没有发送,并且正在准备发送的 ,超过这个窗口的,接收端忙不过来,就不能发送了
第二部分的窗口有多大?
NextByteExpected 和 LastByteRead的差其实是还没有被应用层读取的部分占用调MaxRcvBuffer的量,定义为A, 窗口大小其实是MaxRcvBuffer减去A
其中第二部分里面,由于收到的包可能不是顺序的,会出现空档, 只有和第一部分连续的,可以马上进行回复 ,中间空着的部分需要等待,哪怕后面的已经来了(可以看到接收端的窗口出现了虚线和实线的区别)
发送端
接收端
在 发送端 看来,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的
当前的状态
假设4的ACK到了,不幸的是5的ACK丢了,6、7的数据包丢失了,这应该怎么做?
对每一个发送了,但是没有ACK的包,都设有一个定时器,超过了一定的时间就重新尝试,但是这个超时的时间如何进行评估呢,这个时间不宜过短,时间必须大于往返时间RTT,否则将会引起不必要的重传,也不宜过长,这样的超时时间变长,访问就变慢了
RTT(Round-Trip Time): 往返时延。在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。
估计往返时间需要TCP通过 采样RTT的时间,然后进行加权平均 ,计算出来一个值,并且这个值还是随着网络的状况 不断变化 的,我们成为 自适应重传算法
如果过一段时间,5、6、7都超时了,就会重新发送,接收方发现5原来接受过,于是丢弃5;6收到了,发送ACK,要求下一个是7,7不幸又丢了,当7再次超时的时候,有需要重传的时候, TCP的策略是超时间隔加倍,每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前的两倍,两次超时就说明网络环境差,不适合频繁反复发送
超时触发重传存在的问题是,超时周期可能相对较长
有一个 快速重传的机制 ,当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间隔,于是发送三个冗余的ACK,客户端收到后,在定时器过期之前,重传丢失的报文段
例如,接收方发现6、8、9都已经接收了,7还没来,那肯定是丢了,于是发送三个6的ACK要求下一个是7,客户端收到3个ACK就会发现7的包确实又丢了,不再等待超时,马上重发
SACK ,这种方式需要在TCP头加一个SACK的东西,可以将缓存的地图发送给发送方,例如有了ACK6、ACK8、ACK9就会知道7丢了
在对于包的确认中,同时会携带一个窗口的大小
假设窗口不变,始终为9,4的确认来的时候,会右移一个,这个时候第13个包也可以发送了
这个时候,假设发送端发送过猛,会将第三部分的10、11、12、13全部发送完毕,之后就停止发送了,未发送可发送部分为0
当对于包5的确认到达的时候,在客户端相当于窗口滑动了一格,这个时候才可以有更多的包可以发送了,接下来14可以被发送
如果接收方实在处理太慢,导致缓存中没有了空间,可以通过确认信息修改窗口的大小,甚至可以设置为0,让发送端暂时停止发送
假设接收端应用一直不读取缓存中的数据,当数据包6被确认后,窗口大小就会减小一个变为8
这个时候可以看到,接收端的窗口并没有向右移动,只是简单地将左边的标记右移一格,窗口大小变为8
如果接收端一直不处理数据,则随着确认包越来越多,窗口越来越小直到为0
如果情况变成这样, 发送方会定时发送窗口探测数据包,看看是否有机会调整窗口的大小 ,当接收方比较慢的时候,要防止低能窗口综合征, 不要空出一个字节就告诉发送方 ,然后立马被填满,可以当窗口太小的时候,不更新窗口,直到达到一定大小,或者缓冲区一般为空的时候再更新窗口
拥塞控制同样通过窗口的大小来控制,滑动窗口是为了防止发送方把接收方缓存塞满,而拥塞窗口是为了不把网络填满
LastByteSent - LastByteAcked <= min{滑动窗口, 拥塞窗口}
TCP协议是不知道真个网络路径都是什么,TCP包常被比喻为往一个谁管理灌水TCP拥塞控制就是在不堵塞,不丢包的情况下,尽量发挥带宽
网路通道的容量 = 带宽 x 往返延迟
假设往返时间为8s,发送的过程4s,返回的时间4s,每个包1024byte,过了8s,8个包都发出去了,其中4个已经到达了接收端,但是ACK还在路上,不能算是发送成功了,5-8后四个包还在路上没被接收,这个时候,整个管道刚好被撑满
如果我们在这个基础上再将窗口调大一点,会出现什么现象?
如果从发送端到接收端会经过四个设备,每个设备处理包的时间需要1s,所以4个包的话,总共的处理时间为4s,如果窗口调大,也就有可能增加发送速度,单位时间内,会有更多的包到达这些中间设备,那么处理中的设备会丢弃到多余的包,这是我们不想看到的
这个时候,我们可以为这四台设备增加缓存,处理不过来的包在队列里等待,这样就不会丢失了,但是缺点是会增加时间,在之前我们分析过只需要4s一个包即可到达发送端,但是进入缓存中多余的包肯定到达的时间是要超过4s的,如果这个时候发送方还是没有收到ACK那么就会触发超时重传, TCP的拥塞控制就是为了处理包的丢失和超时重传
一条TCP连接的开始,cwnd设置为一个报文段,一次只能发送一个,当收到这个确认的时候,cwnd +1,于是一次能够发送2个,当这两个的确认到来的时候,每个确认的cwnd + 1 ,两个确认的cwnd就可以 +2,现在可以发送4个, 这是指数级别的增长,但是有一个值sshthresh为65535字节,当超过这个值的时候不要增长得这么快了,可能快满了,再慢下来
于是,每收到一个确认后,cwnd增长1/cwnd,一共发送8个的话,当8个确认到来的时候,每个确认增加1/8,八个确认一共cwnd + 1,于是一次能够发送9个,变成了 线性增长 ,但是肯定有一天会满,这个时候就会出现拥堵,就需要慢慢等待包的处理
拥塞的一种形式是丢包,需要超时重传 ,这个时候
重新开始慢启动,这样的话,只要超时重传就感觉会回到解放前
快速重传 ,当接收端发现丢了一个中间包的时候,发送三次前一个包的ACK,于是发送端就会快速重传,不必等待超时再重传,TCP认为这种情况不严重,因为大部分没丢,只丢了一小部分
正是这种知道该快还是慢的情况下,使得时延很重要的情况下,反而降低了速度,但是 拥塞控制还是存在问题
为了优化这两个问题,有了TCP BBR拥塞算法,它企图找到一个平衡点,通过不断的加快发送速度,将管道填满,但是不会填满中间设备的缓存,因为这样时延会增加,这个平衡的时点可以很好的达到高带宽和低时延的平衡
2023-07-25 广告