将 char* 转换为 struct
Posted
技术标签:
【中文标题】将 char* 转换为 struct【英文标题】:Converting char* to struct 【发布时间】:2017-06-30 07:22:26 【问题描述】:代码here中有一行:
struct iphdr * iph = (struct iphdr *)buffer;
在ProcessPacket
函数中,其中buffer
的类型为char*
。 buffer
已在主函数中由 recvfrom
赋予值。简单字符串(buffer
)如何转换为结构,如何安全提取数据?
iphdr:
struct iphdr
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 tos;
__u16 tot_len;
__u16 id;
__u16 frag_off;
__u8 ttl;
__u8 protocol;
__u16 check;
__u32 saddr;
__u32 daddr;
/*The options start here. */
;
【问题讨论】:
buffer
中的数据不是字符串,这是要知道的重要一点。相反,它只是一大块字节,恰好对应于结构。
如果以这种方式写入缓冲区:unit8_t *buffer = (uint8_t *)&yourStruct
,那么返回就可以了:yourStruct* str = (yourStruct *)buffer
。否则,应该执行memcpy
。
@Some程序员老兄recvfrom
编辑一串数据来填充其中的数据包数据。如果它对应于结构,那么打印编辑的字符串也必须包含标题数据,而不仅仅是字符串,但这不会发生。
您收到的数据是二进制数据,不对应任何可以打印的字符。它(再次)是 not 字符串(在 C 或 C++ 意义上)。这就像从文件中读取非文本二进制数据,您希望能够打印它吗?还是将数据用作字符串?
【参考方案1】:
buffer
不是 string
。它是一个指向 raw 二进制数据的指针。 recvfrom
用原始 IP/TCP 帧(又名数据包)填充(在本例中,见下文)buffer
。因此,buffer
的第一个 sizeof(iphdr)
字节是 IP-header 结构:iphdr
。这正是博客作者使用您提供的 sn-p 的原因:
struct iphdr * iph = (struct iphdr *)buffer;
如果包含 IP 标头选项,则标头的实际大小为 iph->ihl*4
。
然后在ProcessPacket
(在博客中)中检查标头的协议字段(iph->protocol
),以确定数据包包含的传输协议。
如果使用的传输协议是TCP,则可以使用 (sn-p from blog) 提取 TCP 标头(以及后来的数据):
unsigned short iphdrlen = iph->ihl*4;
struct tcphdr *tcph = (struct tcphdr*)(buffer + iphdrlen);
原始帧
博客作者使用以下方法创建了套接字:
sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
第一个参数AF_INET
表示你想要IPv4数据包(与AF_INET6
相反,IPv6)。
第二个参数告诉socket
你想要原始帧
第三个参数 (IPPROTO_TCP
) 确保您获得 TCP 帧
或者,如果您想要 UDP 帧,您可以使用:
sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_UDP);
如果您贪婪并且想要每个数据包使用(请在使用之前阅读帧格式!):
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
【讨论】:
出色的解释,但在我的问题的上下文中,另一个答案更合适。谢谢。【参考方案2】:我认为由于在您的程序过程中执行了以下两行代码,代码引入了未定义的行为:
unsigned char *buffer = (unsigned char *)malloc(65536);
...
struct iphdr *iph = (struct iphdr*)buffer;
buffer
是指向保留为unsigned char*
的内存块的指针,然后将其转换为struct iphdr
类型的指针;然而struct iphdr
很可能与char*
有不同的对齐限制,这是未定义的行为(例如,参见this online c11 draft standard):
6.3.2.3 指针
(7) 指向对象类型的指针可以转换为指向对象类型的指针 不同的对象类型。如果结果指针不正确 为引用类型对齐,则行为未定义。 ...
虽然它可能会起作用(这仍然是 UB 的选项之一),但也可能是程序以您不希望的方式运行。
我建议将信息复制到正确对齐的struct iphdr
-object 中:
unsigned char *buffer = (unsigned char *)malloc(65536);
...
struct iphdr iphobj;
memcpy(&iphobj,buffer,sizeof(struct iphdr));
...
然后注意对象的生命周期。
请注意,您标记了代码 C
和 C++
,两种语言有不同的规则(例如,关于 malloc
的结果的显式转换,这在 C++ 中是必需的,但在 C 中不鼓励)。
但是关于 UB,我很确定代码在 C 和 C++ 这两种语言中都引入了 UB。
【讨论】:
这仅适用于编译器强制执行严格别名的情况吗?我见过很多 C 代码示例,人们会这样做,但我有点困惑为什么。【参考方案3】:首先要了解的是,无论(struct iphdr *)
的演员如何,内存中的位都保持完全相同。只是你现在说buffer
现在被视为指向struct iphdr
的指针,而不是以前的指针。您只是告诉编译器用不同的眼镜查看这些位,然后进行相应的解释。编译器突然发现buffer
变成了struct iphdr *
。并说“好的”就是这样。重要的是您确切地知道 buffer
是什么并将其转换为正确的类型。
如果您愿意,您可以将buffer
类型转换为int *
(或任何其他指针类型),编译器不会说什么。虽然你以后会遇到问题。
【讨论】:
以上是关于将 char* 转换为 struct的主要内容,如果未能解决你的问题,请参考以下文章
将 NSStrings 转换为 C 字符并从 Objective-C 调用 C 函数
无法将 [Struct] 类型的值快速转换为 [string] 类型
BigQuery - 将通用 JSON 转换为 STRUCT