TCP校验和详细讲解和计算

首先看看TCP校验和计算原理:

TCP校验字段占两个字节如0x62,0x0d,实际上是这样计算来的:
对方IP+我方IP+报文长度+信息长度+0x06(协议类型)+
TCP各字段长度以及数据各字段和(注意此时将对应的校验和字段--TCP的第17,18位,实际对应数组下标的16,17位--都设为0x00.同时
每两个字节组合为一个数值如0x01,0x02组合为0x0102,数据abc对应为0x61,0x62,0x63,则组合成0x6162,0x6300)

上述算出来的校验和(设为resule)假如超过0xffff的话,将超过的部分添加到result的低位去再用0xffff减去该值,如:0x54321最终应成为:0xffff-(0x4321+0x05)

其次根据抓包工具抓住的TCP报文我们看到下列图片中已经有一个包含了双方IP,TCP报文以及信息的报文,根据图片中给出的信息,依照上述原理手动计算一个TCP校验和:






本地Ip:0xc0,0xa8,0x9f,0x01;
对方IP:0xc0,0xa8,0x9f,0x82
TCP字段:0x04, 0xc6, 0x87, 0x01, 0x4b, 0xd7, 0x89, 0x9f, 0x4e, 0x3b, 0x90, 0xae, 0x50, 0x18, 0xff, 0xff, 0xeb, 0x69, 0x00, 0x00
从上述实际报文可见该TCP的校验和为eb69
将对应的校验和字段设为0x00后TCP报文实际为:
0x04, 0xc6, 0x87, 0x01, 0x4b, 0xd7, 0x89, 0x9f, 0x4e, 0x3b, 0x90, 0xae, 0x50, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
数据字段abc,对应0x61,0x62,0x63;
TCP报文长度为20,信息长度为3,23化为16进制即0x17
所以result=(0xc0a8+0x9f01)+(0xc0a8+0x9f82)+(0x04c6+0x8701+0x4bd7+0x899f+0x4e3b+0x90ae+0x5018+0xffff+0x0000+0x0000)+(0x17+0x06)+(0x6162+0x6300)
得出result=0x7148f,将超出0xffff的位加到低位上去,reuslt=0xffff-(0x148f + 0x0007)=0xeb69正好和抓包工具显示的校验和字段是一样的.

最后就是给出一个计算校验和的函数了

#include <stdio.h>
INT8U sum[2]={0};			//两个保存校验和的全局变量
INT8U Info[30]={0};			//保存实际数据的全局变量
/*
*计算校验和的函数,传递过来的参数分别是:指向纯TCP报文的的指针data,指向本地IP的指针mIP,指向对方IP的指针yIP,实际要传送的数据长度 length
*该函数计算TCP校验和字段的实际填充信息,通过全局变量数组sum[2]返回.
*/
void checksum(INT8U *data,INT8U *mIP,INT8U *yIP,INT16U length)
{
	INT16U csum; 
	INT16U i;
	INT32U dat[30]={0};				//保存TCP的的数组
	INT32U shu[30]={0};				//保存信息的数组
	INT16U temp;
	INT32U result,temp1,temp2,temp3,temp4=0,temp5=0;

	//将IP字段从数组中取出组合成新的数据
	temp1=((INT16U)mIP[0]<<8)+(INT16U)mIP[1]+((INT16U)mIP[2]<<8)+(INT16U)mIP[3];
	temp2=((INT16U)yIP[0]<<8)+(INT16U)yIP[1]+((INT16U)yIP[2]<<8)+(INT16U)yIP[3];
	
	//数据长度+协议类型
	temp3=(INT16U)(length)+0x06;	
	for(i=0 ; i<length ; i++)
	{
		if(i%2==0)				//将偶数位左移放到高位
		{
			dat[i]=(INT16U)data[i]<<8;
			shu[i]=(INT16U)Info[i]<<8;
		}
		else					//奇数位不变
		{
			dat[i]=(INT16U)data[i];
			shu[i]=(INT16U)Info[i];
		}		
	} 
	dat[16]=0x00;				//校验和字段初始置0
	dat[17]=0x00;
	for(i=0 ; i<length ; i++)
	{
		temp4=dat[i]+temp4;
		temp5=shu[i]+temp5;
	}
	result=temp1+temp2+temp3+temp4+temp5;	
	if(result>0xffff)
	{
		temp=result/0xffff;//得到高位 
		csum=(INT16U)result;//得到低位
		csum=0xffff-(csum+temp);
	}
	//将计算出来的数据和高位和低位分别放到数组对应字段中
	sum[0]=csum>>8;
	sum[1]=(INT8U)csum;
}

int main()
{
	INT8U length=20;
	INT8U i,j=0;
	INT8U info[30]={'a','b','c'};
	INT8U mIP[]={0xc0,0xa8,0x9f,0x01};
	INT8U yIP[]={0xc0,0xa8,0x9f,0x82};
	INT8U PSH_ACK[30]={0x04,0xc6,0x87,0x01,0x4b,0xd7,0x89,0x9f,0x4e,0x3b,						//一个TCP数组
		0x90,0xae,0x50,0x18,0xff,0xff,0x00,0x00,0x00,0x00};
	for(i=0;i<30;i++)												//数据放到全局变量 
		Info[i]=info[i];
	while(Info[j]!=0)
		j++; 													//获得实际信息的长度
	length=length+j;
	checksum(psh,mIP,yIP,length);									
	printf("\n\nlength:%x%x\n\n",sum[0],sum[1]); 									//看答案是否是620d
	return 0;
}
注意其中的INT8U等类型是这样定义的

typedef unsigned char  INT8U;                   /* 无符号8位整型变量                        */
typedef unsigned short INT16U;                  /* 无符号16位整型变量                       */
typedef unsigned int   INT32U;                  /* 无符号32位整型变量                       */

该计算TCP校验和的函数最初是为"嵌入式IP_TCP协议栈"的实现而编写的,其环境是

操作系统:    uCOS

网络芯片:    enc28j60

硬件环境:    使用Proteus + LPC2124

开发环境:    Keil V0.3

当放到嵌入式keil v0.3的时候,将相关头文件和库函数如printf和stdio.h更换掉,或干脆不用.

当然因为它最初是在vs里完成的也可以用在windows里需要计算校验和的地方.

当然该函数有不尽人意的地方,比如完全可以将信息放到TCP数组的里面,这样可以减少很多累赘的语句提高运行速度,希望各位IT好友提出其他的建议和意见大笑!

你可能感兴趣的:(TCP校验和详细讲解和计算)