上一篇已经说明了TCP为什么这么复杂,因为既要保证可靠性, 同时又要尽可能提高性能。在保证可靠性的机制中,讲解了序列号(按序到达)和确认应答机制,今天继续讲解校验和机制,讲解TCP校验和机制之前,先来学学IP包的校验和,为什么呢?????因为TCP 首部检验和与 IP 首部校验和的计算方法相同,在程序中使用同一个函数来计算。
关于IP报文头部结构及字段说明,在之前的RS | IP报文头部篇章已将将结果了,这儿不再过多赘述,具体的知识点请点击超链接查看学习。
这次的重点是IP包的校验和计算原理和实际验证
一、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);
}
算法是不是感觉不太好理解,好吧,下面通过实际抓包验证。认真看哟!!!
三、实例验证
从下图的WireShark抓包分析可以得出IP头部的一些信息
把以上所抓取的信息划到表格中如下:[hide]
再把表格里的信息转换成二进制如下:
按照过程进行:
1、首先将检验和置零;
2、然后将IP头,都化为二进制数;
3、如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4、最后将该16bit的值取反,存入校验和字段0x64b6
如下是计算步骤:
我们将计算出来的74e1和第一张图中的数值对比一下,是否一样?
即:通过某种的算法,得出了最终的值,这个值即为应填充的校验和。
当接收到IP数据包时,要检查IP头是否正确,则对IP头进行校验,方法同上,只是把头部校验和的“0000 0000 0000 0000”换成正式的头部校验和,最终求和为FFFF,取反为0000,表示数据在传输过程中没问题,正确的。
[/hide]开篇已经说过,TCP 首部检验和与 IP 首部校验和的计算方法相同,在程序中使用同一个函数来计算。
需要注意的是,由于 TCP 首部中不包含源地址与目标地址等信息,为了保证 TCP 校验的有效性,在进行 TCP 校验和的计算时,需要增加一个 TCP 伪首部的校验和,具体的我们将在下一篇博文中继续讲述。