C/C++解析char缓冲区存储的IP数据包出错,如何排查?
问题
我有一个存储在char缓冲区中的IP数据包:
char packet[] = "450000CC68C4000001114C4F0A0A0A0AEFFFFFFADEA9076C00B832074D2D534541524348202A20485454502F312E310D0A484F53543A203233392E3235352E3235352E3235303A313930300D0A4D414E3A2022737364703A646973636F766572220D0A4D583A20310D0A53543A2075726E3A6469616C2D6D756C746973637265656E2D6F72673A736572766963653A6469616C3A310D0A555345522D4147454E543A20476F6F676C65204368726F6D652F3130372E302E353330342E313130204D6163204F5320580D0A0D0A";
我尝试使用netinet/ip.h中的ip结构体解析该IP头部,但打印IP长度等信息时出现垃圾值:
#include <netinet/ip.h> void decodeIPPacket(const char *packet) { struct ip *ipHdr = (struct ip*)(long)packet; printf("decodeIPPacket:: IP Len: %d", ipHdr->ip_len); printf("decodeIPPacket:: srcip: %u", ipHdr->ip_src.s_addr); printf("decodeIPPacket:: dstip: %u", ipHdr->ip_dst.s_addr); }
输出结果:
decodeIPPacket:: IP Len: 12336
decodeIPPacket:: srcip: 808464432
decodeIPPacket:: dstip: 825307440
请问我哪里出错了?
解答
核心错误:你存储的是十六进制字符串,不是原始二进制IP数据包
你的packet数组里是ASCII格式的十六进制字符(比如"45"是字符'4'和'5'),而struct ip需要直接读取原始二进制字节(比如0x45这个单字节值)。直接把字符串指针强转成struct ip*,会让结构体成员读取到错误的字节组合,自然得到垃圾值。解决方法
- 先把十六进制字符串转成二进制字节流
遍历字符串,每两个字符对应一个十六进制数,转换成对应的字节值,存入unsigned char缓冲区。示例转换代码:#include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/ip.h> #include <arpa/inet.h> void hexToBin(const char *hexStr, unsigned char *binBuf, size_t bufSize) { size_t hexLen = strlen(hexStr); if (hexLen % 2 != 0 || bufSize < hexLen/2) { return; // 输入格式错误或缓冲区不足 } for (size_t i=0; i<hexLen; i+=2) { sscanf(hexStr+i, "%2hhx", &binBuf[i/2]); } } - 处理网络字节序转换
IP头部的字段(比如ip_len、ip_src.s_addr)都是网络字节序(大端),如果你的主机是小端字节序,必须用ntohs()(转换16位)、ntohl()(转换32位)转换成主机字节序再输出,否则数值会不正确。 - 修正解析函数
用转换后的二进制缓冲区解析,同时优化IP地址的输出格式:void decodeIPPacket(const unsigned char *packet) { struct ip *ipHdr = (struct ip*)packet; printf("decodeIPPacket:: IP Len: %d\n", ntohs(ipHdr->ip_len)); printf("decodeIPPacket:: srcip: %s\n", inet_ntoa(ipHdr->ip_src)); printf("decodeIPPacket:: dstip: %s\n", inet_ntoa(ipHdr->ip_dst)); } - 完整调用示例
运行后会得到正确的输出:int main() { char packet[] = "450000CC68C4000001114C4F0A0A0A0AEFFFFFFADEA9076C00B832074D2D534541524348202A20485454502F312E310D0A484F53543A203233392E3235352E3235352E3235303A313930300D0A4D414E3A2022737364703A646973636F766572220D0A4D583A20310D0A53543A2075726E3A6469616C2D6D756C746973637265656E2D6F72673A736572766963653A6469616C3A310D0A555345522D4147454E543A20476F6F676C65204368726F6D652F3130372E302E353330342E313130204D6163204F5320580D0A0D0A"; size_t binSize = strlen(packet)/2; unsigned char *binPacket = malloc(binSize); if (binPacket) { hexToBin(packet, binPacket, binSize); decodeIPPacket(binPacket); free(binPacket); } return 0; }decodeIPPacket:: IP Len: 204 decodeIPPacket:: srcip: 10.10.10.10 decodeIPPacket:: dstip: 239.255.255.250
- 先把十六进制字符串转成二进制字节流
内容的提问来源于stack exchange,提问作者vishal kumar
相关产品推荐
相关产品推荐

