这是一个简单的程序,展示了我们通常如何在编写套接字时将 struct sockaddr *
类型转换为 struct sockaddr_in *
或 struct sockaddr_in6 *
程序。
#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's Guide to Network Programming还在第 10 页中推荐了这一点。
To deal with struct sockaddr, programmers created a parallel structure: struct sockaddr_in (“in” for “Internet”) to be used with IPv4.
And this is the important bit: a pointer to a struct sockaddr_in can be cast to a pointer to a struct sockaddr and vice-versa. So even though connect() wants a struct sockaddr*, you can still use a struct sockaddr_in and cast it at the last minute!
但来自 another question 的讨论,看来这只是一个 hack,不是符合 C 标准的有效 C 代码。
具体参见 AnT's answer提到,
As for the popular technique with casts between struct sockaddr *, struct sockaddr_in * and struct sockaddr_in6 * - these are just hacks that have nothing to do with C language. They just work in practice, but as far as C language is concerned, the technique is invalid.
因此,如果我们用于进行套接字编程的这种技术(以及书籍也推荐的技术)无效,那么重写上述代码的有效方法是什么,以便它也是符合 C 标准的有效 C 代码?
最佳答案
So if the way we do socket programming (and what is also recommended by the books) is a hack, what is the correct way to rewrite the above code so that it is also a valid C code as per the C standard?
TL;DR:继续执行您在示例中展示的内容。
您提供的代码在语法上似乎是正确的。在某些情况下,它可能会或可能不会表现出未定义的行为。它是否这样做取决于 getaddrinfo()
的行为。
在满足所有功能要求并且比您提供的标准技术更好地防止未定义行为的情况下,没有办法在 C 中执行此操作。这就是为什么它是标准技术。这里的问题是该函数必须支持所有可能的地址类型,包括尚未定义的类型。它可以将套接字地址指针声明为 void *
,这不需要转换,但实际上不会改变任何给定程序是否表现出未定义的行为。
就其本身而言,getaddrinfo()
的设计正是考虑到了这种用法,因此如果对结果使用预期的转换会导致不当行为,这就是它的问题。此外,getaddrinfo()
不是 C 标准库的一部分——它(仅)由 POSIX 标准化,POSIX 也包含 C 标准。因此,仅根据 C 来分析该功能就证明了不适当的超聚焦。尽管强制转换仅针对 C 就引起了一些关注,但您应该预料到在 getaddrinfo()
和其他使用 struct sockaddr *
的 POSIX 网络函数的上下文中,强制转换为正确的特定地址类型和访问引用的对象会产生可靠的结果。
此外,我认为 AnT 对您的其他问题的回答过于简单化且过于消极。我在考虑要不要写一个反差答案。
关于c - 使用有效的 C 代码将 struct sockaddr * 转换为 struct sockaddr_in6 * 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39432774/