TCP效验和(Checksum)的原理和实现

概述

TCP校验和(Checksum)是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。

TCP校验和覆盖TCP首部和TCP数据,而IP首部中的校验和只覆盖IP的首部,不覆盖IP数据报中的任何数据。TCP校验和、IP校验和的计算方法是基本一致的,除了计算的范围不同。

TCP的校验和是必需的,而UDP的校验和是可选的。TCP和UDP计算校验和时,都要加上一个12字节的伪首部。

伪首部

首先解释下伪首部的概念,伪首部的数据都是从IP数据报头获取的。其目的是让TCP检查数据是否已经正确到达目的地,只是单纯为了做校验用的。

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

伪首部共有12字节(前96Bits),包含如下信息:源IP地址、目的IP地址、保留字节(置0)、传输层协议号(TCP是6)、TCP报文长度(报头+数据)。

伪首部是为了增加TCP校验和的检错能力:如检查TCP报文是否收错了(目的IP地址)、传输层协议是否选对了(传输层协议号)等。

校验和计算

 

RFC 793的TCP校验和定义:
The checksum field is the 16 bit one’s complement of the one’s complement sum of all 16-bit words in the header and text. If a segment contains an odd number of header and text octets to be checksummed, the last octet is padded on the right with zeros to form a 16-bit word for checksum purposes. The pad is not transmitted as part of the segment. While computing the checksum, the checksum field itself is replaced with zeros.

 

上述的定义说得很明确:
首先,把伪首部、TCP报头、TCP数据分为16位的字,如果总长度为奇数个字节,则在最后增添一个位都为0的字节。把TCP报头中的校验和字段置为0(否则就陷入鸡生蛋还是蛋生鸡的问题)。
其次,用反码相加法累加所有的16位字(进位也要累加)。
最后,对计算结果取反,作为TCP的校验和。

 

举个例子来解释该校验方法: 

这里写图片描述


1、首先将检验和置零;
2、然后将TCP伪首部部分,TCP首部部分,数据部分都划分成16位的一个个16进制数
3、将这些数逐个相加,记得溢出的部分加到最低位上,这是循环加法:
 0xc0a8+ 0x0166+……+0x0402=0x9b49
4、最后将得到的结果取反,则可以得到检验和位0x64b6

 

校验和反码求和 的实现、

发送方:原码相加 ,并将高位叠加到低位,取反 ,得到反码求和结果,放入校验和
接收方:将所有原码 相加,高位叠加, 如全为1,则正确 

下面为C实现较为原始的checksum算法,代码中对于算法做了比较详细的注释:
 

unsigned short
checksum(unsigned short * addr, int count)



{
long sum = 0;
/*
计算所有数据的16bit对之和
*/
    while( count > 1  )  {
        /*  This is the inner loop */
        sum += *(unsigned short*)addr++;



        count -= 2
    }  



/* 如果数据长度为奇数,在该字节之后补一个字节(0),
   然后将其转换为16bit整数,加到上面计算的校验和
  中。
 */
    if( count > 0 ) {
        char left_over[2] = {0};
        left_over[0] = *addr;
        sum += * (unsigned short*) left_over;
    }  

/*  将32bit数据压缩成16bit数据,即将进位加大校验和
  的低字节上,直到没有进位为止。
 */
    while (sum>>16)
        sum = (sum & 0xffff) + (sum >> 16);
  
   /*返回校验和的反码*/
   return ~sum;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值