首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用有效的C代码将sockaddr *转换为结构sockaddr_in6 *的正确方法是什么?

用有效的C代码将sockaddr *转换为结构sockaddr_in6 *的正确方法是什么?
EN

Stack Overflow用户
提问于 2016-09-11 04:11:45
回答 4查看 2.5K关注 0票数 2

下面是一个简单的程序,它展示了我们通常如何在编写套接字程序时将struct sockaddr *类型转换为struct sockaddr_in *struct sockaddr_in6 *

代码语言:javascript
运行
复制
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main()
{
    struct addrinfo *ai;

    printf("sizeof (struct sockaddr): %zu\n", sizeof (struct sockaddr));
    printf("sizeof (struct sockaddr_in): %zu\n", sizeof (struct sockaddr_in));
    printf("sizeof (struct sockaddr_in6): %zu\n", sizeof (struct sockaddr_in6));

    if (getaddrinfo("localhost", "http", NULL, &ai) != 0) {
        printf("error\n");
        return EXIT_FAILURE;
    }

    if (ai->ai_family == AF_INET) {
        struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
        printf("IPv4 port: %d\n", addr->sin_port);
    } else if (ai->ai_family == AF_INET6) {
        struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
        printf("IPv6 port: %d\n", addr->sin6_port);
    }

    return 0;
}

Beej网络编程指南还在第10页中建议这样做。

为了处理sockaddr,程序员创建了一个并行结构:structsockaddr_in(“in”表示“Internet”)与IPv4一起使用。 这是重要的一点:指向struct sockaddr_in的指针可以转换为指向sockaddr的指针,反之亦然。因此,即使connect()需要一个sockaddr*,您仍然可以使用structsockaddr_in并在最后一分钟对其进行转换!

但从另一个问题的讨论来看,这似乎只是一次黑客攻击,并不是C标准所规定的有效C代码。

特别是,请参阅提到的蚂蚁的回答

至于流行的sockaddr *、struct sockaddr_in *和struct sockaddr_in6 *之间的转换技术--这些只是与C语言无关的黑客。他们只是在实践中工作,但就C语言而言,这种技术是无效的。

因此,如果我们用来进行套接字编程的技术(以及书籍中推荐的技术)是无效的,那么根据C标准重写上述代码的有效方法是什么?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-09-11 04:51:20

那么,如果我们进行套接字编程的方式(以及书中也推荐的方法)是黑客,那么重写上述代码的正确方法是什么,以便按照C标准也是一个有效的C代码?

TL;DR:继续做你在你的例子中提到的事情。

您提供的代码在语法上似乎是正确的。在某些情况下,它可能会或不会表现出不明确的行为。它是否这样做取决于getaddrinfo()的行为。

在C中没有办法做到这一点,因为它满足了所有的功能需求,并且比您所介绍的标准技术更好地防止未定义的行为。这就是为什么这是标准技术。这里的问题是,函数必须支持所有可能的地址类型,包括尚未定义的类型。它可以将套接字地址指针声明为void *,这不需要转换,但实际上不会更改任何给定程序是否具有未定义的行为。

getaddrinfo()本身而言,它的设计正是考虑到了这种用法,所以如果在结果上使用预期的强制转换允许错误行为,那么这就是它的问题。此外,getaddrinfo()不是C标准库的一部分--它是由POSIX标准化的(仅),它也包含了C标准。因此,仅从C的角度来分析该功能就显示了一个不适当的超聚焦。尽管强制转换本身就引起了一些关注,但是您应该期望在getaddrinfo()和其他使用struct sockaddr *的POSIX网络函数的上下文中,转换到正确的特定地址类型并访问引用的对象会产生可靠的结果。

另外,我认为AnT对你另一个问题的回答过于简单化,过于消极。我正在考虑是否要写一个对比鲜明的答案。

票数 4
EN

Stack Overflow用户

发布于 2016-09-11 04:42:20

POSIX标准保证指向任何类型的套接字的指针都可以转换为struct sockaddr*。因此,您可以将指向struct sockaddr*的任何类型的套接字的指针转换为在bind()connect()中使用;库知道要检查哪些位。您还可以检查套接字的sa_family字段,看看它到底是什么,假设它包含有效的数据,然后转换为适当的指针类型。如果您需要分配足够大的内存块来安全地存储任何类型的套接字,请使用sockaddr_storage。从sockaddr_storage*到任何其他套接字指针的转换都保证正确对齐,并且包含套接字家族的字段保证仍然工作。

要从IPv6中获取sockaddr_in套接字,可以将IPv4地址转换为IPv6符号并使用getaddrinfo()。但是,现代的查找函数可能会为您提供一个包含IPv4和IPv6套接字的链接列表。

票数 1
EN

Stack Overflow用户

发布于 2016-09-11 04:48:24

答案是man getaddrinfosys/socket.hman getaddrinfo使用一个普通的struct sockaddr提供了rational

给定节点和服务(标识Internet主机和服务),getaddrinfo()返回一个或多个addrinfo结构,每个结构包含可以在绑定(2)或连接(2)调用中指定的Internet地址。getaddrinfo()函数将gethostbyname(3)和getservbyname(3)函数提供的功能组合到一个接口中,但与后面的函数不同,getaddrinfo()是可重入的,允许程序消除IPv6 4与IPv6 6的依赖。

只有一个struct sockaddr。看起来,各种类型都只是在透明的联合中使用,以提供所需的任何struct sockaddr_X。例如:

代码语言:javascript
运行
复制
/* This is the type we use for generic socket address arguments.

   With GCC 2.7 and later, the funky union causes redeclarations or
   uses with any of the listed types to be allowed without complaint.
   G++ 2.7 does not support transparent unions so there we want the
   old-style declaration, too.  */
#if defined __cplusplus || !__GNUC_PREREQ (2, 7) || !defined __USE_GNU
# define __SOCKADDR_ARG         struct sockaddr *__restrict
# define __CONST_SOCKADDR_ARG   const struct sockaddr *
#else
/* Add more `struct sockaddr_AF' types here as necessary.
   These are all the ones I found on NetBSD and Linux.  */
# define __SOCKADDR_ALLTYPES \
  __SOCKADDR_ONETYPE (sockaddr) \
  __SOCKADDR_ONETYPE (sockaddr_at) \
  __SOCKADDR_ONETYPE (sockaddr_ax25) \
  __SOCKADDR_ONETYPE (sockaddr_dl) \
  __SOCKADDR_ONETYPE (sockaddr_eon) \
  __SOCKADDR_ONETYPE (sockaddr_in) \
  __SOCKADDR_ONETYPE (sockaddr_in6) \
  __SOCKADDR_ONETYPE (sockaddr_inarp) \
  __SOCKADDR_ONETYPE (sockaddr_ipx) \
  __SOCKADDR_ONETYPE (sockaddr_iso) \
  __SOCKADDR_ONETYPE (sockaddr_ns) \
  __SOCKADDR_ONETYPE (sockaddr_un) \
  __SOCKADDR_ONETYPE (sockaddr_x25)

# define __SOCKADDR_ONETYPE(type) struct type *__restrict __##type##__;
typedef union { __SOCKADDR_ALLTYPES
            } __SOCKADDR_ARG __attribute__ ((__transparent_union__));
# undef __SOCKADDR_ONETYPE
# define __SOCKADDR_ONETYPE(type) const struct type *__restrict __##type##__;
typedef union { __SOCKADDR_ALLTYPES
            } __CONST_SOCKADDR_ARG __attribute__ ((__transparent_union__));
# undef __SOCKADDR_ONETYPE
#endif

我还没有涉猎过所有的宏汤,但看来这两种类型的汤都是安全的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39432774

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档