当发送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))