gpt4 book ai didi

C - 创建一个 sockaddr 结构

转载 作者:太空宇宙 更新时间:2023-11-04 03:19:01 24 4
gpt4 key购买 nike

我正在尝试创建一个 void mksockaddr(int af, int proto, char addr[], struct sockaddr* dst) 来创建一个 sockaddr 结构,这是我所做的:

void sockaddr(int af, int port, char addr[], struct sockaddr* dst) {
if (af == AF_INET) {
struct sockaddr_in s;
s.sin_family = af;
s.sin_port = htons(port);
inet_pton(af, addr, &s.sin_addr);
memcpy(dst, &s, sizeof(s));
} else {
struct sockaddr_in6 s;
s.sin6_family = af;
s.sin6_port = htons(port);
s.sin6_flowinfo = 0;
inet_pton(af, addr, &s.sin6_addr);
memcpy(dst, &s, sizeof(s));
}
}

这似乎对 AF_INET (IPv4) 没有问题,我可以 bind() 没有任何问题,但是当我尝试使用 AF_INET6 时,bind() 给出me 无效参数。

这是我用来bind()的代码:

int sock_fd = socket(AF_INET6, SOCK_RAW, proto);
struct sockaddr sin;
sockaddr(AF_INET6, proto, src, &sin);
if(bind(sock_fd, &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket: %s\n", strerror(errno));
exit(1);
} // got Invalid argument

但是,如果我自己构造一个 sockaddr_in6,我可以 bind() 就好了:

struct sockaddr_in6 sin;
sin.sin6_port = htons(proto);
sin.sin6_family = AF_INET6;
inet_pton(AF_INET6, src, &sin.sin6_addr);
if(bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket.\n");
exit(1);
} // work just fine

所以我将函数创建的 sockaddr 转换回 sockaddr_in6,我可以看到除了 sin6_scope_id 之外所有字段都是相同的。据我了解,sin6_scope_id 并不重要,除非我正在处理链接本地 IPv6 地址。

我在这里遗漏了什么吗?

最佳答案

从 C 的角度来看,为了确保您的代码按预期工作,调用者必须在 dst 参数中传递指向正确结构类型的有效指针。您的示例不会这样做。相反,它声明了一个 struct sockaddr,并传递一个指向它的指针。类型 struct sockaddr 本身永远不会用作实际对象的类型,并且它对于所有可能的地址类型来说都不够大。特别是,它对于 IPv6 地址来说不够大。

另一方面,POSIX 比标准 C 对一致性程序的要求更快更宽松。这对于套接字地址尤其明显。它定义了一个类型 struct sockaddr_storage 来准确地满足您的目的:它足够大并且具有适当的对齐方式来保存任何支持的套接字地址类型的数据。文档特别提到了它在一般支持 IPv4 和 IPv6 方面的用途。 POSIX 还支持在不同的套接字地址指针类型之间进行强制转换,尽管这会导致违反 C 的结构别名规则。

因此,我将重写您的函数以明确使用 struct sockaddr_storage,并且我将通过适当的转换进一步简化我的代码。此外,我会让我的函数告诉我地址​​结构的可用大小,它只包含初始化的那部分:

void populate_sockaddr(int af, int port, char addr[],
struct sockaddr_storage *dst, socklent_t *addrlen) {

if (af == AF_INET) {
struct sockaddr_in *dst_in4 = (struct sockaddr_in *) dst;

*addrlen = sizeof(*dst_in4);
memset(dst_in4, 0, *addrlen);
dst_in4->sin_family = af;
dst_in4->sin_port = htons(port);
inet_pton(af, addr, &dst_in4->sin_addr);

} else if (af == AF_INET6) {
struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *) dst;

*addrlen = sizeof(*dst_in6);
memset(dst_in6, 0, *addrlen);
dst_in6->sin6_family = af;
dst_in6->sin6_port = htons(port);
// unnecessary because of the memset(): dst_in6->sin6_flowinfo = 0;
inet_pton(af, addr, &dst_in6->sin6_addr);
} // else ...
}

然后你可以像这样使用它:

struct sockaddr_strorage addr;
socklen_t addrlen;

populate_sockaddr(af, port, src, &addr, &addrlen);
if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
// ...
}

请注意,将 &addr 转换为类型 struct sockaddr * 完全是例行公事。

关于C - 创建一个 sockaddr 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48328708/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com