一、8位校验和的实现
1.发送端计算8位校验和的步骤:
(1)把校验和字段设置为0。
(2)把需要校验的数据看成以8位为单位的数字组成,依次进行求和,得到的和二进制求反码,再加上1,最终得到校验和。
(3)把得到的结果存入校验和字段中。
2.接收端校验校验和步骤:
(1)把需要校验的内容(包括校验和字段)看成以8位为单位的数字,依次进行二进制反码求和,如果结果是0表示正确,否则表示错误。
3.实现代码:
1 static unsigned char sg_ucSeq = 0;
2 typedef struct
3 {
4 unsigned long hid;
5 unsigned long cid;
6 unsigned char type;
7 unsigned char len;
8 unsigned char checksum;
9 unsigned char seq;
10
11 unsigned char userData[MAX_USER_DATA_SIZE];
12 }DATA_T;
13
14 #define MAX_USER_DATA_SIZE 40
15 #define DATA_HEADER_LEN (sizeof(DATA_T) - MAX_USER_DATA_SIZE)
16
17 unsigned char MakeCheckSum8(DATA_T *pData)
18 {
19 unsigned char ucCheckSum = 0;
20 unsigned char ucNum;
21 unsigned char *pucDat = (unsigned char *)pData;
22
23 // 以1byte为单位依次相加
24 for(ucNum=0;ucNum<pData->len;ucNum++){
25 ucCheckSum += pucDat[ucNum];
26 }
27
28 // 二进制求反码,并加1,得到校验和
29 ucCheckSum = ~ucCheckSum;
30 ucCheckSum++;
31
32 return ucCheckSum;
33 }
34
35 unsigned char CheckData8(DATA_T *pData)
36 {
37 unsigned char ucCheckSum = 0;
38 unsigned char ucNum;
39 unsigned char *pucDat = (unsigned char *)pData;
40
41 for(ucNum=0;ucNum<pData->len;ucNum++){
42 ucCheckSum += pucDat[ucNum];
43 }
44
45 return ucCheckSum;
46 }
47 void create_common_msg(DATA_T *pfdata, unsigned char type, unsigned char *pdata, int len)
48 {
49 memset(pdata, 0, sizeof(DATA_T));
50
51 pfdata->cid = 0x12345678;
52 pfdata->type = type;
53 pfdata->len = DATA_HEADER_LEN + len;
54 pfdata->checksum = 0;
55 pfdata->seq = sg_ucSeq++;
56
57 if (len > 0)
58 {
59 memcpy(pfdata->userData, pdata, len);
60 }
61
62 pfdata->checksum = MakeCheckSum8(pdata);
63
64 return;
65 }
二、16位校验和的实现
1.计算校验和的步骤:
(1)把校验和字段设置为0。
(2)把需要校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和。
(3)把得到的结果存入校验和字段中。
另外UDP、TCP数据报的长度可以为奇数字节,因为计算时是16位为单位,所以此时计算校验和时需要在最后增加一个填充字节0(只是计算校验和用,不发送出去)。
2.接收端校验校验和步骤:
(1)把需要校验的内容(包括校验和字段)看成以16位为单位的数字,依次进行二进制反码求和,如果结果是0表示正确,否则表示错误。
3.二进制反码求和步骤:
(1)二进制反码求和,就是先把这两个数取反,然后求和,如果最高位有进位,则向低位进1。
(2)另外,先取反后相加与先相加后取反,得到的结果是一样的。因此实现代码都是先相加,最后再取反。
4.实现代码:
1 static inline unsigned short check_sum(const unsigned short *buf, int size)
2 {
3 unsigned long chksum = 0;
4
5 //16位为单位数字相加
6 while(size>1)
7 {
8 chksum += *buf++; // 对传入的数据以unsigned short方式解析
9 size -= sizeof(unsigned short );
10 }
11
12 //长度奇数情况
13 if(size)
14 {
15 chksum += *((unsigned char *)buf);
16 }
17
18 //高位有进位,进位到低位,下面两行代码保证了高16位为0。
19 while (chksum >> 16)
20 {
21 chksum = (chksum >> 16) + (chksum & 0xffff);
22 }
23
24 //最后取反
25 return (unsigned short )(~chksum);
26 }
三、32位校验和的实现
1.发送端计算32位校验和的步骤:
(1)把校验和字段设置为0。
(2)把需要校验的数据看成以32位为单位的数字组成,依次进行求和,得到的和二进制求反码,再加上1,最终得到校验和。
(3)把得到的结果存入校验和字段中。
2.接收端校验校验和步骤:
(1)把需要校验的内容(包括校验和字段)看成以32位为单位的数字,依次进行二进制反码求和,如果结果是0表示正确,否则表示错误。
3.实现代码:
1 unsigned int make_checksum_32(unsigned char *pdata, int dataLen)
2 {
3 int len;
4 unsigned int chk = 0;
5
6 // 按照小端的方式计算4个字节的checksum
7 len = dataLen;
8 while (len >= 4)
9 {
10 chk += (unsigned int)pdata[0]; // ---- ---- ---- pdata[0]
11 chk += ((unsigned int)pdata[1] << 8); // ---- ---- pdata[1] ----
12 chk += ((unsigned int)pdata[2] << 16); // ---- pdata[2] ---- ----
13 chk += ((unsigned int)pdata[3] << 24); // pdata[3] ---- ---- ----
14
15 pdata += 4;
16 len -= 4;
17 }
18
19 if (len > 0)
20 {
21 if (len == 1)
22 {
23 chk += (unsigned int)pdata[0];
24 }
25 else if (len == 2)
26 {
27 chk += (unsigned int)pdata[0];
28 chk += ((unsigned int)pdata[1] << 8);
29 }
30 else
31 {
32 chk += (unsigned int)pdata[0];
33 chk += ((unsigned int)pdata[1] << 8);
34 chk += ((unsigned int)pdata[2] << 16);
35 }
36 }
37
38 chk = ~chk;
39 chk++;
40
41 return chk;
42 }