当我在计算校验和时,试图把char*转为short*计算,发生了错误。

正确计算校验和的过程应该是:

1
2
3
4
5
6
7
8
9
10
11
char *pBuffer;
memcpy(pBuffer,this,sizeof(Ipv4));
int sum = 0;
for(int i = 0; i < 10; i++) {
sum += (int)((unsigned char)pBuffer[i*2] << 8);
sum += (int)((unsigned char)pBuffer[i*2+1]);
}
while(sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
this->checksum = htons(~sum);

但我在看到第5、6行时想:这里是在干嘛,直接用short*不可以吗。于是就改成了下面这样:

1
2
3
4
5
6
7
8
9
10
short *pBuffer;
memcpy(pBuffer,this,sizeof(Ipv4));
int sum = 0;
for(int i = 0; i < 10; i++) {
sum += (int)((unsigned short)pBuffer[i]);
}
while(sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
this->checksum = htons(~sum);

结果不行。

原因:char转为short\的操作是不对的。

我使用的计算机是小端存储的,而网络传输的数据是大端序。在读取short数据时,会按照小端序读取,这意味着计算机会认为这个数字是按照小端序存储的,读取时会把位于高地址的解释为高位。

假设数据为 12 34 56 78,我希望的正确计算为0x1234+0x5678,但上面错误的代码会将这个变为0x3412+0x7856。

验证1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <cstring>
#include <cstdio>

int main() {
char b[4];
char *a = &b[0];
b[0] = 0x12;
b[1] = 0x34;
b[2] = 0x56;
b[3] = 0x78;
short *pBuffer = (short*)(a);
int *p = (int*)(a);
printf("%.2x ", p[0]);
memcpy(pBuffer, a, 4);
int sum = 0;
for (int i = 0; i < 2; i++) {
sum += pBuffer[i];
printf("%.2x ", pBuffer[i]);
}
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
}

输出为:

1
78563412 3412 7856

验证2:

1
2
3
4
5
6
7
8
9
10
short *pBuffer;
memcpy(pBuffer,this,sizeof(Ipv4));
int sum = 0;
for(int i = 0; i < 10; i++) {
sum += (int)htons((unsigned short)pBuffer[i]);
}
while(sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
this->checksum = htons(~sum);

bingo!

留言

2021-11-22
Contents

⬆︎TOP