【问题标题】:Sending integer over socket while avoid breaking strict aliasing rule通过套接字发送整数同时避免违反严格的别名规则
【发布时间】:2021-02-01 03:19:54
【问题描述】:

我正在尝试在 C 中通过网络发送一个整数(更具体地说,uint32_t 字节)。根据 Stack Overflow 上的其他示例和答案,我相信我正确地发送了数据;但是,在接收字节时,我无法在不破坏 strict aliasing rule 的情况下将它们转换/转换为 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) 中通过套接字传输整数时最有效地避免违反严格的别名规则。

【问题讨论】:

  • 小花絮:将数组大小传递给函数时,请尝试使用sizeof 推导它,以避免输入两次相同的幻数(8192):recv(socket, buf, sizeof buf, 0);
  • 您的反馈是错误的——因为数据是用char类型写入的(不是从其他类型复制的),它没有有效类型,所以自动获取读取它的有效类型。没有严格的别名违规。
  • @ChrisDodd 您的反馈是错误的,它是一个 char 数组,有效类型的东西不能像那样工作。此外,更完整的程序也可能具有未对齐的访问权限,而这些才是真正的无声杀手。
  • @AnttiHaapala:数据是recv写的,所以没有有效类型。根据 6.5 的任何时候,您将数据复制为 chars,它保持相同的有效类型,它不会获得 char 的有效类型。对齐可能是个问题,但与严格的别名无关。
  • 你使用什么协议? 'client_sock' 建议使用 TCP,在这种情况下,您发布的代码无论如何都会被破坏。

标签: c windows sockets integer winsock


【解决方案1】:

您的原始代码在严格别名方面没有任何问题——无论是谁给您的反馈都是不正确的。它确实遇到的问题是对齐——不能保证char buff[8192]; 将与char 以外的任何类型正确对齐(尽管它可能会如此)。您可以通过使用 malloc 分配缓冲区来避免该问题:

char *buf = malloc(8192);

malloc 将始终返回为任何类型适当分配的内存。然后你的代码

recv(socket, buf, 8192, 0);
uint32_t test = ntohl(*(uint32_t*)buf);
printf("%d\n", (int)test);

是安全的,但您可能应该检查一下 malloc 是否成功。

【讨论】:

    【解决方案2】:

    把它分成几行:

    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);
    

    【讨论】:

    • @entry934h 我看不出你的代码有什么问题,除了它是丑陋的......也许你有一个复合文字的编译器错误......:D
    • @user253751 不,他们不是。
    【解决方案3】:

    代替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]);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 2020-04-11
      • 2015-01-16
      • 1970-01-01
      相关资源
      最近更新 更多