zoukankan      html  css  js  c++  java
  • TCP

    TCP包头格式

    def132524f225cc400994f545150ddcd.png
    首先,源端口号和目标端口号是不可少的.
    接下来是包的序列号.
    然后就是确认序号.
    接下来就是状态位.例如SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接
    还有一个重要的是窗口大小.TCP要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力.

    TCP 的三次握手

    64b9847002e0e29c96f141533a835cfc.png
    一开始,客户端和服务端都处于CLOSED状态.先是服务端主动监听某个端口,处于LISTEN状态.然后客户端主动发起连接SYN,之后处于SYN-SENT状态.服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态.客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了.服务端收到ACK的ACK之后,处于ESTABLISHED状态,因为它也一发一收了.

    TCP的四次挥手

    4b15ec76165a5f158ee7ef4ec25219fe.png
    断开的时候,我们可以看到A发了一个断开的数据包给B,然后就进入了FIN_WAIT_1的状态,B收到后就发送一个回应,就进入了CLOSE_WAIT的状态

    A收到了B的数据包之后就进入了FIN_WAIT_2的状态,如果这个时候B直接断开,则A将永远在这个状态.TCP协议里面并没有对这个状态的处理,但是Linux有,可以调整TCP_FIN_TIMEOUT这个参数,设置一个超时时间

    如果B没有直接断开,那么就会给A也发送一个表示断开的数据包,请求到达A之后会发送一个答应ACK给B,A会从FIN_WAIT_2状态结束,按理来说到这里A可以断开了,但是如果这个ACK万一B收不到呢?则B会重新发一个表示断开的数据包给A,这个时候如果A已经断开,那么B就再也收不到ACK了,因而TCP协议要求A最后等待一段时间TIME_WAIT,这个时间要足够长,长到如果B没收到ACK的话,能够一直等到B重发,并A再次发送

    等待时间设置为2MSL,MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃

    还有一种异常的情况就是,B超过了2MSL的时间,依然没有收到它发送的FIN的ACK,按照TCP的原理,B当然还是会重发FIN,这个时候A再收到这个包之后,A就直接发送RST,B就知道A早已断开了

    TCP状态机

    将连接建立和断开的这个时序状态综合起来,就是著名的TCP的状态机
    a19e4eed5be91226f6b5edd1b10167ff.png

    累计确认

    在TCP里,接收端会给发送端报一个窗口的大小,叫Advertised window.

    发送端需要保持的数据结构
    fdbaa065755dc607cc22d38dbe1e2a0a.png

    • LastByteAcked:第一部分和第二部分的分界线
    • LastByteSent:第二部分和第三部分的分界线
    • LastByteAcked+AdvertiedWindow:第三部分和第四部分的分界线

    接收端需要保持的数据结构:
    069f0076e0c8bf6d499770077b0e3cf0.png

    • MaxRcvBuffer: 最大缓存的量
    • LstByteRead: 已经接收了,但是还没被应用层读取的
    • NextByteExpected: 第一部分和第二部分的分界线

    NextByteExpected 和 LastByteRead的差其实是还没被应用层读取的部分占用掉的MaxRcvBuffer 的量,我们定义为A.

    AdvertiedWindow其实就是MaxRcvBuffer减去A

    也就是说:AdvertiedWindow = MaxRcvBuffer-((NextByteExpected-1)-LastByteRead).

    顺序问题与丢包问题

    还是刚才的图,在发送端看来,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

    假设4确认到了,不幸的是,5的ACK丢了,6/7的数据包丢了.

    那么这会引起一种是超时重试,另一种是超时间隔加倍.

    超时重试是指,对每一个发送了,但是没有ACK的包,都有一个定时器,超过了一定的时间就重新尝试.这个时间的评估就由自适应重传算法决定.TCP需要采样RTT的时间,然后进行加权平均,算出一个值,而且这个值是要不停变化的,因为网络状况不断的变化.除了采样RTT,还要采样RTT的波动范围,计算出一个估计的超时时间.

    超时间隔加倍指,如果过一段时间,5/6/7都超时了,就会重新发送.接收方发现5原来接受过,于是丢弃5;6收到了,发送ACK,要求下一个是7,7不幸又丢失了.当7再次超时的时候,又需要重传的时候,TCP的策略是超时间隔加倍.每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为钱值的两倍.两次超时,就说明网络环境差,不宜频繁反复发送

    流量控制问题

    在对于包的确认中,同时会携带一个窗口的大小.

    我们假设窗口始终为9.4确认来的时候会右移一个,这个时候第13个包也发送了.
    95431ffd78714da4a37bf76b8e343116.png
    这个时候,假设发送端将10//11/12/13全发送完毕,之后就停止发送
    5e9b0381c49c3478e284db37c5ce4c1b.png
    当对于包5的确认到达的时候,在客户端相当于窗口再滑动一格
    bddb59ebbf7eecc4853cafce0bb1dcc3.jpeg
    如果接收方实在处理太慢,导致缓存中没有空间了,可以通过信息修改窗口的大小,甚至可以设置为0,则发送方将暂时停止发送.

    我们假设一格极端的情况,接收端的应用一直不读取缓存中的数据,当数据包6确认后,窗口大小就不能再是9了,就要缩小一格变为8
    92f66b1556b76c46c669aba232d35a31.jpeg

    这个心的窗口8通过6的确认消息到达发送端的时候,窗口并没平行右移,但LastByteAcked右移了,窗口的大小从9改成了8

    a78f5195ebf9b4f9dc4ea5a9b91e94ba.jpeg

    如果接收端还是一直不处理数据,那么窗口越来越小, 直到0

    150f28d9e745952f5968eff05e3f0ad2.jpeg

    当这个窗口通过包14的确认到达发送端的时候,发送端的窗口也调整为0,停止发送

    3014a6a259f74b0c950bf3067581ac30.jpeg

    这种通过接收方来告诉发送方调整窗口大小的行为叫流量控制

    拥塞控制问题

    滑动窗口(rwnd)是怕发送方把接收方缓存塞满,而拥塞窗口cwnd,是怕把网络塞满

    公式LastByteSent - LastByteAcked <=min{cwnd,rwnd} ,是拥塞窗口和滑动窗口共同控制发送的速度

    TCP拥塞控制就是在不堵塞不丢包的情况下,尽量发挥带宽.

    通道的容量= 带宽* 往返延迟

    TCP的拥塞控制主要来避免两种现象, 包丢失和超时重传.

    一开始的时候TCP发送数据的时候会慢启动,cwnd设置为一个报文段,一次只能发送一个;当收到这一个确认的时候cwnd加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认cwnd加一,两个确认cwnd加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认cwnd加一,四个确认cwnd加四,于是一次能够发送八个.可以看出这是指数性的增长.

    涨到超过ssthresh设置的值,即65535个字节的时候,再慢下来.

    每收到一个确认后,cwnd增加1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加1/8,八个确认一共cwnd增加1,于是一次能够发送九个,变成了线性增长

  • 相关阅读:
    选择排序
    快速排序
    希尔排序
    直接插入排序
    判断三角形的类型
    Unicode编码下: CString 转换为 string
    GDAL中GDALDataType中值与其在C++中数据类型对应
    ftell()
    fseek()
    fopen_s()
  • 原文地址:https://www.cnblogs.com/luozhiyun/p/10260037.html
Copyright © 2011-2022 走看看