zoukankan      html  css  js  c++  java
  • TCP头部分析与确认号的理解

    1、TCP的特点:

    基于字节流
    面向连接
    可靠传输
    缓冲传输
    全双工
    流量控制

    2、头部格式和说明

    图源百度。如下图示,就是TCP包的头部结构。可以看到这个头部最少有4x5=20个字节。

    另外还需要理解TCP协议是承载在IP协议中的。关于IP协议可以参考:http://www.cnblogs.com/xcywt/p/8067521.html

    源端口号和目的端口号:再加上Ip首部的源IP地址和目的IP地址可以唯一确定一个TCP连接
    数据序号:表示在这个报文段中的第一个数据字节序号
    确认序号:仅当ACK标志为1时有效。确认号表示期望收到的下一个字节的序号(这个下面再详细分析)
    偏移:就是头部长度,有4位,跟IP头部一样,以4字节为单位。最大是60个字节
    保留位:6位,必须为0
    6个标志位:
    URG-紧急指针有效
    ACK-确认序号有效
    PSH-接收方应尽快将这个报文交给应用层
    RST-连接重置
    SYN-同步序号用来发起一个连接
    FIN-终止一个连接

    窗口字段:16位,代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16 - 1 = 65535个字节(这个下面再详细分析)

    校验和:源机器基于数据内容计算一个数值,收信息机要与源机器数值 结果完全一样,从而证明数据的有效性。检验和覆盖了整个的TCP报文段:这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证的。

    紧急指针:是一个正偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式
    选项与填充(必须为4字节整数倍,不够补0):
    最常见的可选字段的最长报文大小MSS(Maximum Segment Size),每个连接方通常都在一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段。
    该选项如果不设置,默认为536(20+20+536=576字节的IP数据报)

    3、TCP如何保证可靠性

    1)应用数据被分割成TCP认为最合适发送的数据块。称为段(Segment)传递给IP层
    2)当TCP发出一个段后,它会启动一个定时器,等待目的端确认收到这个报文段。若没有及时收到确认,将重新发送这个报文段
    3)当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送的,通常将推迟几分之一秒。
    4)TCP将保持它首部和数据的校验和,这是一个端到端的校验和,目的是检测数据在传输过程中的任何变化。如果收到段的校验和有差错,TCP将丢弃这个报文也不进行确认(对方就会重复发送了)。
    5)TCP承载与IP数据报来传输,而IP数据报可能会失序,所以TCP的报文段到达时也可能会失序。但是TCP收到数据后会重新排序到正确的顺序(通过序号)。
    6)IP数据报会发生重复,TCP的接收端必须丢弃重复是数据
    7)TCP还能提供流量控制,TCP连接的每一方都有一定大小的缓冲空间

     4、滑动窗口协议(也就是对包头中窗口字段的理解)

    参考1:https://www.cnblogs.com/ulihj/archive/2011/01/06/1927613.html

    参考2:http://blog.chinaunix.net/uid-26275986-id-4109679.html

    先上两个概念:
    通告接收窗口(rwnd):预防应用程序发送的数据超过对方的缓冲区,接收方使用的流量控制。
    拥塞窗口(cwnd):预防应用程序发送的数据超过了网络所能承载的能力。发送方使用的流量控制。
    发送窗口:就是指上面两者的较小值

    由于TCP的全双工的,所以其实TCP双方各自都维护一个发送窗口和接收窗口。

    假设是主机A发送给主机B
    A和B都会维护一个数据帧的序列,这个序列称为窗口。发送方的窗口大小由接收方确定。目的在于控制发送速度。以免接收方的缓存不够大而导致溢出,同时流量控制也可以避免网络拥塞。
    这里其实是指A的发送窗口。


    假设A发送了很多段给B,序号是1-10.这些段会处于种状态:
    1)已发送,已确认
    2)已发送,未确认
    3)等待发送
    4)不允许发送

    正常情况下,每个段都会由4状态->3状态->2状态->1状态。而窗口就是指处于状态2和状态3的总数。
    由2状态->1状态的时候,窗口就会往后滑动一下,表示最近那个4状态的段可以变成3状态了。
    如果接收方一直不确认,那么处于4状态的段将永远不会被发送。
    当窗口满了的时候,4状态的段将不会变成3状态。从而达到了控制发送速度的作用。

    就像上图一样,123处于1状态,456处于2状态,789处于3状态,10以后的处于4状态。而窗口就是指哪个框起来的。这里为6。

    随着发送段被逐一的确认,这个窗口会往右滑动。

    就像一个水池,总体积V,进水速度是s1,出水速度s2。当水池满了就不能再注入了,强行注入会溢出丢失。窗口就是那个水池。

    滑动窗口实现面向流的可靠性:

    1)最基本的传输可靠性来源于确认重传机制
    2)滑动窗口的可靠性也是建立在确认重传机制上的
    3)发送窗口只有收到目的端口对本段发送窗口内字节的ACK确认,才会移动发送串口的左边界。
    4)接收窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有段未收到确认,但是收到了后面段的情况下,窗口不会移动,也不对后续段进行确认。以此确保发送端会对这个数据重传。

    5、关于包头中确认号ack的理解

     确认序号:仅当ACK标志为1时有效。确认号表示期望收到的下一个字节的序号

    这里是拿三次握手之后,开始传输数据了进行分析。

    服务器向客户端发送一个数据包后,客户端收到了这个数据包,会向服务器发送一个确认数据包。

    传输数据的简要过程如下:

    1)发送数据:服务器向客户端发送一个带有数据的数据包。该数据包中的序列号和确认号与建立连接第三步的数据包找那个的序列号和确认号相同。

    2)确认收到:客户端收到该数据包,向服务器发送一个确认数据包。该数据包中,序列号是为上一个数据包中的确认号值。

    而确认号为服务器发送的上一个数据包中的序列号+该数据包中所带数据的大小。

    回复确认收到的ack = 收到了序列号 + 数据的大小(同时也表示下一次期望收到的序号)

    这里我们直接拿Wireshark抓包进行分析:

    实例1:客户端给服务器发送了”xcychongyong” 共13个字节。

    先看,服务器收到的,也就是客户端发送的:seq是10,数据长度是13.

    再来看服务器发送给客户端的确认包:根据上面的说明。ack应该是10 + 13 = 23

     

    实例2:

    如下图,208(就是192.168.0.208)一共向182(就是192.168.0.182)发送了6组数据。

    过滤条件:tcp and (ip.src==192.168.0.182 or ip.dst==192.168.0.182)

    对于182来说:

    第一次回应时ack是4,结果208下一次发送的序号就是4。

    第二次回应时ack是10,结果208下一次发送的序号就是10。

    第三次回应时ack是19,结果208下一次发送的序号就是19。

    以此类推…

     

    再来分析一个互相发送的:

    如图,一共发送了5次:

    第一次182发给208,发的长度是7,seq是1.所以208回复的ack是8。也相当于告诉182:“182,你下次发的时候,序号就从8开始”。看第2个红框,seq就是8.

    第二次208发给182,发的长度是11,seq是1,所以182回复的ack是12。也相当于告诉208:“208,你下次发的时候,序号就从12开始”。看第2个绿框,seq就是12.

    同理,

    182再发送一次给208,seq应该是17  

    208再发送一次给182,seq应该是33

     

    ack表示期望下次接收到的序号。

    那么ack是如何算出来的呢,就是通过收到的序号,和数据长度相加得来。

    假设A收到B过来的数据(seq = 5,len = 15)。len表示数据长度。

    那么A就会回复B,“刚才的数据我已经收到了,你接下来就发序号为20的包给我吧”。这样就保证了数据不会乱序。

     综上,确认号就是下一次将要收到包的序号。同时也等于发送方的序号+数据长度(确认号在ACK标志位有效时才有用。)

  • 相关阅读:
    使用 OpCache 提升 PHP 性能
    在线视频开发博客教程
    修正ECMALL在PHP5.3以上版本中无法开启支付方式的BUG
    第二个小项目的讲解后感言
    mybatis进行分页,使用limit
    mybatis传递多个参数值(转)
    一个关于前端页面的小标签<tbody>
    json-lib的一些过滤操作
    关键字volidate和transient(转)
    Java中实现序列化的两种方式 Serializable 接口和 Externalizable接口
  • 原文地址:https://www.cnblogs.com/xcywt/p/8075623.html
Copyright © 2011-2022 走看看