TCP/UDP报文格式
    TCP 协议为终端设备提供了面向连接的、可靠的网络服务,UDP 协议为终端设备提供了无连接的、不可靠的数据报服务。从上图我们可以看出,TCP 协议为了保证数据传输的可靠性,相对于UDP 报文,TCP 报文头部有更多的字段选项。
首先让我们来看一下 TCP 的报文头部主要字段:
    每个 TCP 报文头部都包含源端口号(source port)和目的端口号(destination port),用于标识和区分源端设备和目的端设备的应用进程。在TCP/IP 协议栈中,源端口号和目的端口号分别与源IP 地址和目的IP 地址组成套接字(socket),唯一的确定一条TCP 连接。
    序列号(Sequence number)字段用来标识TCP 源端设备向目的端设备发送的字节流,它表示在这个报文段中的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则TCP 用序列号对每个字节进行计数。序列号是一个32bits 的数。
    既然每个传输的字节都被计数,确认序号(Acknowledgement number,32bits)包含发送确认的一端所期望接收到的下一个序号。因此,确认序号应该是上次已成功收到的数据字节序列号加1。
    TCP 的流量控制由连接的每一端通过声明的窗口大小(windows size)来提供。窗口大小用数据包来表示,例如Windows size=3, 表示一次可以发送三个数据包。窗口大小起始于确认字段指明的值,是一个16bits 字段。窗口大小可以调节。
    校验和(checksum)字段用于校验TCP 报头部分和数据部分的正确性。
    最常见的可选字段是 MSS(Maximum Segment Size,最大报文大小)。MSS指明本端所能够接收的最大长度的报文段。当一个TCP 连接建立时,连接的双方都要通告各自的MSS 协商可以传输的最大报文长度。我们常见的MSS有1024(以太网可达1460 字节)字节。
    相对于 TCP 报文,UDP 报文只有少量的字段:源端口号、目的端口号、长度、校验和等,各个字段功能和TCP 报文相应字段一样。
    UDP 报文没有可靠性保证和顺序保证字段,流量控制字段等,可靠性较差。当然,使用传输层UDP 服务的应用程序也有优势。正因为UDP 协议较少的控制选项,在数据传输过程中,延迟较小,数据传输效率较高,适合于对可靠性要求并不高的应用程序,或者可以保障可靠性的应用程序像DNS、TFTP、SNMP 等;UDP 协议也可以用于传输链路可靠的网络。
UDP报文
UDP报头定长为8B。按顺序为:
1UInt16 源端口号
关于端口号有一些规定,服务器端通常用熟知端口号,通常在0-1023之间。而客户端用随机的端口号,其范围在4915265535之间。
2UInt16 目的端口号
3,UInt16 总长度
包括报头和数据的长度之和。显然在[865535]区间。
4UInt16检验和
如果不需要检验和,就取0。如果需要检验和,那么其算法为:
1)构造12B的伪报头
UInt32 IP+UInt32 目的IP+Byte 0+Byte 17+UInt16 总长度(上面所说的)
2UDP的报头
3UDP的数据部分
unsafe UInt16 计算校验和(UInt16* buffer, int size)
{
    Int32 cksum = 0;
    int counter;
    counter = 0;
    while (size > 0)
    {
        UInt16 val = buffer[counter];
        cksum += Convert.ToInt32(buffer[counter]);
        counter += 1;
        size -= 1;
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (UInt16)(~cksum);
}
构造UDP报文(含有检验和)需要的输入参数包括:
UInt32 IPUInt32 目的IPUInt16 源端口号、UInt16 目的端口号、Byte[] 数据部分、Uint16 数据部分的长度
public Byte[] 构造UDP数据报(UInt32 IP, UInt32 目的IP, UInt16 源端口号, UInt16 目的端口号, Byte[] 数据部分, UInt16 数据部分的长度)
{
   
  UInt16 总长度 = (UInt16)(8 + 数据部分的长度);
    UInt16 校验和 = 0;
    //网络字节顺序
    IP=(uint)IPAddress.HostToNetworkOrder((int)IP);
    目的IP = (uint)IPAddress.HostToNetworkOrder((int)目的IP);
    源端口号 = (ushort)IPAddress.HostToNetworkOrder((Int16)源端口号);
    目的端口号 = (ushort)IPAddress.HostToNetworkOrder((Int16)目的端口号);
    总长度 = (ushort)IPAddress.HostToNetworkOrder((Int16)总长度);
    //
    Byte[] udpbytes = new Byte[8 + 数据部分的长度];
    BitConverter.GetBytes(源端口号).CopyTo(udpbytes, 0);//填入源端口号
    BitConverter.GetBytes(目的端口号).CopyTo(udpbytes, 2);//填入目的端口号
   
    BitConverter.GetBytes(tcp ip协议4和6总长度).CopyTo(udpbytes, 4);//填入总长度
   
    BitConverter.GetBytes(校验和).CopyTo(udpbytes, 6);//校验和
    数据部分.CopyTo(udpbytes, 8);
//下面是计算校验和.数据部分如果不是偶数字节则补一个字节.
    byte[] 伪报文 = new byte[20 + (数据部分的长度+1) / 2*2];//确保偶数字节
    BitConverter.GetBytes(IP).CopyTo(伪报文, 0);//填入源端口号
    BitConverter.GetBytes(目的IP).CopyTo(伪报文, 4);//填入目的端口号
    伪报文[8] =0;//填充0.网络字节顺序
    伪报文[9] = 17;//UDP协议号             
    BitConverter.GetBytes(总长度).CopyTo(伪报文, 10);//填入总长度
    udpbytes.CopyTo(伪报文, 12);
    //string ss = 网络字节串(伪报文 );
    unsafe
    {
        fixed (byte * pt = 伪报文)
        {
            ushort* pu = (ushort*)pt;
            校验和 = 计算校验和(pu, 伪报文.Length/2);
        }
    }
   
    //不知道为什么不对校验和进行网络字节顺序的调整
    BitConverter.GetBytes(校验和).CopyTo(udpbytes, 6);//校验和
    return udpbytes;
}
TCP UDP报文解析

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。