当我在计算校验和时,试图把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); } }
|
输出为:
验证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!