通过套接字发送整数同时避免违反严格的别名规则
Sending integer over socket while avoid breaking strict aliasing rule
我试图用 C 语言通过网络发送一个整数(更具体地说,uint32_t
字节)。根据 Stack Overflow 上的其他示例和答案,我相信我发送的数据是正确的;然而,当接收到字节时,我在不破坏 strict aliasing rule
的情况下遇到 casting/converting 到 uint32_t
的问题。这是我最初尝试的一个例子:
发件人:
uint32_t num = htonl(100);
char* converted_num = (char*)#
send(client_sock, converted_num, sizeof(num), 0);
接收者:
char buf[8192];
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)&buf);
printf("%d\n", (int)test);
虽然这看起来可行,但我收到反馈说行 uint32_t test = ntohl(*(uint32_t*)&buf);
违反了严格的别名规则。这是我修复严格别名违规的尝试:
char buf[8192];
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)memcpy(&(uint32_t){0}, &buf[0], sizeof(uint32_t)));
printf("%d\n", (int)test);
这会导致程序崩溃,即我的问题是在通过 c 套接字传输整数时如何最有效地避免违反严格的别名规则 (windows)。
把它分成几行:
uint32_t tmp;
memcpy(&tmp, buf + whatever, sizeof tmp);
result = ntohl(tmp);
你也可以把它变成一个内联函数:
static inline uint32_t get_u32(char *from) {
uint32_t tmp;
memcpy(&tmp, from, sizeof tmp);
return ntohl(tmp);
}
...
uint32_t test = get_u32(buf + whatever);
您的原始代码在严格别名方面没有任何问题——无论是谁给您的反馈都是不正确的。 存在的问题是对齐——无法保证 char buff[8192];
对除 char
以外的任何类型都正确对齐(尽管可能会)。您可以通过使用 malloc:
分配缓冲区来避免该问题
char *buf = malloc(8192);
malloc 将始终 return 适合分配给任何类型的内存。然后你的代码
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)buf);
printf("%d\n", (int)test);
是安全的,尽管您可能应该检查 malloc 是否成功。
而不是 char
,使用 uint32_t
并放弃所有转换。使用正确的类型打印并使用 recv()
.
返回的正确类型
//char buf[8192];
uint32_t buf[8192/sizeof(uint32_t)];
//recv(socket, buf, 8192, 0);
ssize_t len = recv(socket, buf, sizeof buf, 0);
if (len != -1) {
if (len % sizeof *buf != 0) { ; } // TBD code to handle non-multiples of 32-bits
len /= sizeof *buf;
for (ssize_t i = 0; i< len; i++) {
//uint32_t test = ntohl(*(uint32_t*)&buf);
//printf("%d\n", (int)test);
printf("%" PRIu32 "\n", buf[i]);
}
}
我试图用 C 语言通过网络发送一个整数(更具体地说,uint32_t
字节)。根据 Stack Overflow 上的其他示例和答案,我相信我发送的数据是正确的;然而,当接收到字节时,我在不破坏 strict aliasing rule
的情况下遇到 casting/converting 到 uint32_t
的问题。这是我最初尝试的一个例子:
发件人:
uint32_t num = htonl(100);
char* converted_num = (char*)#
send(client_sock, converted_num, sizeof(num), 0);
接收者:
char buf[8192];
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)&buf);
printf("%d\n", (int)test);
虽然这看起来可行,但我收到反馈说行 uint32_t test = ntohl(*(uint32_t*)&buf);
违反了严格的别名规则。这是我修复严格别名违规的尝试:
char buf[8192];
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)memcpy(&(uint32_t){0}, &buf[0], sizeof(uint32_t)));
printf("%d\n", (int)test);
这会导致程序崩溃,即我的问题是在通过 c 套接字传输整数时如何最有效地避免违反严格的别名规则 (windows)。
把它分成几行:
uint32_t tmp;
memcpy(&tmp, buf + whatever, sizeof tmp);
result = ntohl(tmp);
你也可以把它变成一个内联函数:
static inline uint32_t get_u32(char *from) {
uint32_t tmp;
memcpy(&tmp, from, sizeof tmp);
return ntohl(tmp);
}
...
uint32_t test = get_u32(buf + whatever);
您的原始代码在严格别名方面没有任何问题——无论是谁给您的反馈都是不正确的。 存在的问题是对齐——无法保证 char buff[8192];
对除 char
以外的任何类型都正确对齐(尽管可能会)。您可以通过使用 malloc:
char *buf = malloc(8192);
malloc 将始终 return 适合分配给任何类型的内存。然后你的代码
recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)buf);
printf("%d\n", (int)test);
是安全的,尽管您可能应该检查 malloc 是否成功。
而不是 char
,使用 uint32_t
并放弃所有转换。使用正确的类型打印并使用 recv()
.
//char buf[8192];
uint32_t buf[8192/sizeof(uint32_t)];
//recv(socket, buf, 8192, 0);
ssize_t len = recv(socket, buf, sizeof buf, 0);
if (len != -1) {
if (len % sizeof *buf != 0) { ; } // TBD code to handle non-multiples of 32-bits
len /= sizeof *buf;
for (ssize_t i = 0; i< len; i++) {
//uint32_t test = ntohl(*(uint32_t*)&buf);
//printf("%d\n", (int)test);
printf("%" PRIu32 "\n", buf[i]);
}
}