程序问答   发布时间:2022-06-02  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名?

开发过程中遇到将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名的问题如何解决?下面主要结合日常开发的经验,给出你关于将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名的解决方法建议,希望对你解决将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名有所启发或帮助;

在过去的十年中,C和C ++编译器比sockaddr设计接口甚至编写C99时要复杂得多。作为其一部分,已理解的“未定义行为”的 目的_已经改变。回顾过去,未定义的行为通常旨在涵盖_硬件_实现之间关于操作语义的分歧。但是如今,最终归功于许多组织希望停止编写FORTRAN并有能力支付编译器工程师来实现这一目标,未定义的行为是编译器用来 _推断代码 的事情。左移是一个很好的例子:C99 6.5.7p3,4(为清楚起见重新排列了一下)读取

因此,例如,1u << 33UB在unsigned int32位宽的平台上。该委员会之所以没有定义,是因为在这种情况下,不同的cpu架构的左移指令执行不同的操作:有些始终产生零,有些减少了类型宽度(x86)的模数,有些减少了模数的位数。 (ARM),并且至少有一种历史上常见的体系结构会陷入陷阱(我不知道是哪一种,但这就是为什么它是未定义且未指定的原因)。但是现在,如果你写

unsigned int left_shift(unsigned int x, unsigned int y)
{ return x << y; }

在具有32位的平台上unsigned int,知道上述UB规则的编译器将 调用该函数时推断出y必须具有介于0到32之间的值。它将把该范围输入到过程间分析中,并使用它来执行诸如在调用方中删除不必要的范围检查之类的操作。如果程序员有理由认为它们不是 不必要的,那么,现在您开始了解为什么这个主题如此蠕虫。

有关针对未定义行为目的进行的更改的更多信息,请参阅LLVM人们关于该主题的三部分文章(1 23)。

现在您已经了解了,我实际上可以回答您的问题。

在消除了一些不相关的并发症之后struct sockaddr,这些是struct sockaddr_in,和的定义struct sockaddr_storage

struct sockaddr {
    uint16_t sa_family;
};
struct sockaddr_in { 
    uint16_t sin_family;
    uint16_t sin_port;
    uint32_t sin_addr;
};
struct sockaddr_storage {
    uint16_t ss_family;
    char __ss_storage[128 - (sizeof(uint16_t) + sizeof(unsigned long))];
    unsigned long int __ss_force_alignment;
};

这是穷人的子类。它是C语言中的一个普遍用法。您定义了一组结构,它们都具有相同的初始字段,这是一个代码号,用于告诉您实际上已通过了哪种结构。回想过去,每个人都希望,如果您分配并填写了一个struct sockaddr_in,向上转换为struct sockaddr,并将其传递给例如connect,实现connect可以struct sockaddr安全地取消对指针的引用以检索该sa_family字段,得知它正在查看asockaddr_in并将其抛回,然后继续。C标准一直说取消引用struct sockaddr指针会触发未定义的行为-自C89以来,这些规则未更改- 但每个人都希望 在这种情况下 这样做是安全的 __因为无论您使用的是哪种结构,它都是相同的“加载16位”指令。 这就是POSIX和windows文档谈论对齐的原因。早在1990年代编写这些规范的人就认为, 实际上 可能造成麻烦的主要方式是,如果您结束发布未对齐的内存访问。

但是该标准的文本中没有任何关于加载指令或对齐的内容。它是这样说的(C99§6.5p7+脚注):

struct类型仅与自身“兼容”,声明的变量的“有效类型”是其声明的类型。所以您显示的代码…

struct sockaddr_storage addrStruct;
/* ... */
case AF_INET: 
{
    struct sockaddr_in * tmp = (struct sockaddr_in *)&addrStruct;
    tmp->sin_family = AF_INET;
    tmp->sin_port = htons(port);
    inet_pton(AF_INET, addr, tmp->sin_addr);
}
break;

…具有未定义的行为, 即使 幼稚的代码生成将按预期运行,编译器也可以从中进行推断。现代编译器可能由此推断出,case AF_INET 。它将整个块删除为死代码,并随之而来。

那么,您如何sockaddr安全地工作呢?最简单的答案是“随便使用getaddrinfo和”getnameinfo。他们为您解决了这个问题。

但是,也许你需要工作,一个地址族,比如AF_UNIX,即getaddrinfo不处理。在大多数情况下,您只需声明地址族类型正确的变量,然后 _仅_在调用带有struct sockaddr *

int connect_to_unix_socket(const char *path, int type)
{
    struct sockaddr_un sun;
    size_t plen = strlen(path);
    if (plen >= sizeof(sun.sun_path)) {
        errno = EnameTOolONG;
        return -1;
    }
    sun.sun_family = AF_UNIX;
    memcpy(sun.sun_path, path, plen+1);

    int sock = socket(AF_UNIX, type, 0);
    if (sock == -1) return -1;

    if (connect(sock, (struct sockaddr *)&sun,
                offsetof(struct sockaddr_un, sun_path) + plen)) {
        int save_errno = errno;
        close(sock);
        errno = save_errno;
        return -1;
    }
    return sock;
}

实施connect有通过一些跳铁圈,使这个安全的,但是这不是你的问题。

魂斗罗对方的回答,有 一个情况下,你可能想要使用sockaddr_storage;与getpeername和一起getnameinfo在需要同时处理IPv4和IPv6地址的服务器中。这是一种知道要分配多少缓冲区的简便方法。

#ifndef NI_IDN
#define NI_IDN 0
#endif
char *get_peer_hostname(int sock)
{
    char addrbuf[sizeof(struct sockaddr_storage)];
    socklen_t addrlen = sizeof addrbuf;

    if (getpeername(sock, (struct sockaddr *)addrbuf, &addrlen))
        return 0;

    char *peer_hostname = malloc(MAX_HOSTname_LEN+1);
    if (!peer_hostname) return 0;

    if (getnameinfo((struct sockaddr *)addrbuf, addrlen,
                    peer_hostname, MAX_HOSTname_LEN+1,
                    0, 0, NI_IDN) {
        free(peer_hostname);
        return 0;
    }
    return peer_hostname;
}

(我也可能写过struct sockaddr_storage addrbuf,但是我想强调一点,我实际上不需要addrbuf直接访问的内容。)

最后要注意的:如果BSD人已经确定sockaddr结构只是一个 有点不同......

struct sockaddr {
    uint16_t sa_family;
};
struct sockaddr_in { 
    struct sockaddr sin_base;
    uint16_t sin_port;
    uint32_t sin_addr;
};
struct sockaddr_storage {
    struct sockaddr ss_base;
    char __ss_storage[128 - (sizeof(uint16_t) + sizeof(unsigned long))];
    unsigned long int __ss_force_alignment;
};

…由于“包含上述类型之一的聚合或联合”规则,上流和下流本来可以很好地定义。如果您想知道如何在新的C代码中处理此问题,请继续。

解决方法

继上一个问题之后,我对此代码感到很好奇-

case AF_INET: 
    {
        struct sockaddr_in * tmp =
            reinterpret_cast<struct sockaddr_in *> (&addrStruct);
        tmp->sin_family = AF_INET;
        tmp->sin_port = htons(port);
        inet_pton(AF_INET,addr,tmp->sin_addr);
    }
    break;

在问这个问题之前,我在SO上搜索了相同的主题,并得到了关于该主题的混合答案。说使用联合来完成这项任务,但是对于接受的答案的评论也再次有所不同。


微软在相同结构的文档中说-

Opengroup的文档状态-

套接字的手册页也说相同-


我已经使用这两种类型转换看到多种实现CC++语言在野外,现在我不确定的事实,哪一个是正确的,因为有一些职位,矛盾与以上权利-

那么,哪种安全和正确的方法来填充sockaddr_storage结构?这些指针强制转换安全吗?还是联合方法?我也知道该getaddrinfo()调用,但是对于上述仅填充结构的任务而言,这似乎有点复杂。memcpy还有另一种推荐的方式,这样安全吗?

大佬总结

以上是大佬教程为你收集整理的将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名全部内容,希望文章能够帮你解决将围绕sockaddr_storage和sockaddr_in进行转换将破坏严格的别名所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: