zoukankan      html  css  js  c++  java
  • TCP头校验和计算算法详解

    我就不管是按“位”(bit)取反相加,还是 按“1的补码”相加了,总之
    就是把需要进行校验的“字串”加(+)起来,把这相加的 结果取反当做
    校验和” (Checksum), 比如,相加的结果是0101,那么“校验和”就
    1010,验证的时候呢,就是 0101+1010 = 1111 ,取反后, 就是0
    ——如果验证得“零”(0),就是正确的!

    先将checksum字段置零,然后按16位分组,计算2进制反码和,最后再求和的反码!

    为了计算一份数据报的IP检验和,首先把检验和字段置为0。然后,对首部中每个16bit进行二进制反码求和(整个首部看成是由一串16bit的字组成),结果存在检验和字段中。当收到一份IP数据报后,同样对首部中每个16bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。


    当发送IP包时,需要计算IP报头的校验和:

    1、  把校验和字段置为0;

    2、  对IP头部中的每16bit进行二进制求和;

    3、  如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;

    4、  将该16bit的值取反,存入校验和字段。

    ◆当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反 的结果是否为0,是则正确,否则有错。

    算法:

    SHORT checksum(USHORT* buffer, int size)
    
    {
    
    unsigned long cksum = 0;
    
     
    
    while(size>1)
    
    {
    
        cksum += *buffer++;
    
        size -= sizeof(USHORT);
    
    }
    
    if(size)
    
    {
    
        cksum += *(UCHAR*)buffer;
    
    }
    
    cksum = (cksum>>16) + (cksum&0xffff);  //将高16bit与低16bit相加
    
    cksum += (cksum>>16);             //将进位到高位的16bit与低16bit 再相加
    
     
    
    return (USHORT)(~cksum);
    
    }

    实例:

    IP头:  

                  45 00    00 31

                  89 F5    00 00

                  6E 06    00 00(校验字段)

                  DE B7   45 5D       ->    222.183.69.93

                  C0 A8   00 DC     ->    192.168.0.220

    计算:   

        4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4

        0003 + 22C4 = 22C7

         ~22C7 = DD38      ->即为应填充的校验和

    当接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:

    计算:

        4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC

        0003 + FFFC = FFFF

         ~FFFF = 00000     ->正确

    TCP首部检验和与IP首部校验和的计算方法相同,在程序中使用同一个函数来计算。

    需要注意的是,由于TCP首部中不包含源地址与目标地址等信息,为了保证TCP校验的有效性,在进行TCP校验和的计算时,需要增加一个TCP伪首部的校验和,定义如下:

    struct 
    
    {
    
    unsigned long saddr; //源地址
    
    unsigned long daddr; //目的地址
    
    char mbz;//置空
    
    char ptcl; //协议类型
    
    unsigned short tcpl; //TCP长度
    
    }psd_header;



    然后我们将这两个字段复制到同一个缓冲区SendBuf中并计算TCP校验和:

    memcpy(SendBuf,&psd_header,sizeof(psd_header)); 
    
    memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header));
    
    tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));
  • 相关阅读:
    bzoj2733 永无乡 平衡树按秩合并
    bzoj2752 高速公路 线段树
    bzoj1052 覆盖问题 二分答案 dfs
    bzoj1584 打扫卫生 dp
    bzoj1854 游戏 二分图
    bzoj3316 JC loves Mkk 二分答案 单调队列
    bzoj3643 Phi的反函数 数学 搜索
    有一种恐怖,叫大爆搜
    BZOJ3566 概率充电器 概率dp
    一些奇奇怪怪的过题思路
  • 原文地址:https://www.cnblogs.com/RodYang/p/3265515.html
Copyright © 2011-2022 走看看