通过iovec经Unix域套接字收发uint32_t数据接收值异常问题
问题根因
你拿到的随机值和字节类型转换没有任何关系,你打印的根本不是接收到的业务数据,而是iov_base这个指针本身存储的内存地址。
现代操作系统默认开启地址空间随机化(ASLR),每次程序运行时栈上分配的变量地址都会变化,加上你用%d(对应int类型)打印长度为8字节(64位系统下)的指针本身就属于未定义行为,自然每次运行都会得到毫无规律的随机数。
修复方案
1. 修正接收端缓冲区定义
不要用char数组硬接uint32_t类型数据,直接定义同类型变量作为接收缓冲区,从根源避免对齐问题:
// 接收端代码 uint32_t recv_request_id = 0; struct iovec io = { .iov_base = &recv_request_id, .iov_len = sizeof(uint32_t) }; msg.msg_iov = &io; msg.msg_iovlen = 1; // 必须显式设置iovec的数量,漏写会导致内核读错内存
2. 调用recvmsg后正确打印数据
iov_base只是指向缓冲区的指针,必须先完成数据接收、确认收满4字节后,直接打印缓冲区变量本身,不要打印指针:
ssize_t ret = recvmsg(sock_fd, &msg, 0); if (ret == sizeof(uint32_t)) { // 正确打印接收到的uint32_t值 printf("received request_id = %u\n", recv_request_id); }
特殊情况:必须用char数组接收的处理方式
如果你因为业务逻辑必须用char dup[4]作为缓冲区,接收完成后需要做指针类型强转再解引用拿到数值,注意同主机Unix域套接字不需要做网络字节序转换,通信两端字节序完全一致:
// 收完数据后转换 uint32_t parsed_id = *(uint32_t*)dup; printf("parsed request_id = %u\n", parsed_id);
额外注意事项
- 真要打印指针地址时,必须用
%p格式符,并且把指针强转为void*类型,用%d打指针是典型的未定义行为,32/64位系统下都会出现值错乱。 - 如果你用的是
SOCK_STREAM类型的Unix域套接字,和TCP一样是字节流语义,单次recvmsg调用不保证能收满你发的4字节,需要循环接收直到读满指定长度,避免半包问题。
内容的提问来源于stack exchange,提问作者guygrinberger
相关产品推荐
相关产品推荐

