我有一个sockaddr_storage对象,我需要用用户提供的值填充它。请注意,用户可以提供AF_INET
或AF_INET6
作为填充结构的域名。
void fill(sockaddr_storage &addrStruct, int domain,
const char addr[], const int port)
{
std::memset(&addrStruct, 0, sizeof(addrStruct));
switch(domain) {
case AF_INET: addrStruct.sin_family = AF_INET;
addrStruct.sin_port= htons(port);
inet_pton(AF_INET, addr, addrStruct.sin_addr);
case AF_INET6: ....
....
....
default: ....
}
}
非常确定这不起作用,因为addrStruct
的类型为struct sockaddr_storage
,这些成员存在于struct sockaddr_in
中。我也试过static_cast<sockaddr_in>(addrStruct).sin_port
和类似的但是再次没有用。那么我应该如何填充这个结构,以便它保持有效值,同时尊重铸造结构的对齐。
答案 0 :(得分:4)
您需要先使用适当的地址结构,然后填写:
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port= htons(port);
然后,在您完成后,memcpy()
进入sockaddr_storage
:
memcpy(reinterpret_cast<char *>(&addrStruct),
reinterpret_cast<char *>(&sin), sizeof(sin));
请注意,您应该事先将完整的sockaddr_storage
缓冲区清零,就像您一样。
另一种方法是定义您自己的union
所有地址结构,包括sockaddr_storage
,然后初始化相应的union
成员。
答案 1 :(得分:2)
使用getaddrinfo而不是inet_*
系列的地址转换功能几乎总是更好。它会为您完成sockaddr
个对象的所有分配和初始化 - 您不必自己弄乱sockaddr_storage
。它无缝地处理IPv4和IPv6,它处理&#34; multihomed&#34;具有多个地址的主机,它可以(可选)进行DNS查找。
要使用getaddrinfo
,您将完全丢弃您显示的fill
函数。 使用 fill
的代码可能如下所示:
struct sockaddr_storage ss;
fill(&ss, AF_INET, "10.0.0.21", 25);
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
perror("socket");
return -1;
}
if (connect(sock, (struct sockaddr*)&ss, sizeof(struct sockaddr_in)) {
perror("10.0.0.21:25");
return -1;
}
/* use connected sock here */
用
替换它struct addrinfo hints;
struct addrinfo *rp, *result;
memset(hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
int err = getaddrinfo("10.0.0.21", "25", &hints, &result);
if (err) {
if (err == EAI_SYSTEM) {
fprintf(stderr, "10.0.0.21:25: %s\n", strerror(errno));
} else {
fprintf(stderr, "10.0.0.21:25: %s\n", gai_strerror(err));
}
return 1;
}
int sock;
for (rp = result; rp; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sock == -1)
continue;
if (connect(sock, rp->ai_addr, rp->ai_addrlen))
break;
close(sock);
}
if (rp == 0) {
perror("10.0.0.21:25");
return -1;
}
freeaddrinfo(result);
/* use sock here */
看起来更复杂,但要记住它完全取代你不知道如何写的fill
函数,并且还要记住它将为您无缝地处理DNS。 (如果您有一个具体的理由想要仅处理数字地址,则可以在hints
中设置标记。)
有关更详细的示例用法,请参阅我链接到的联机帮助页。
答案 2 :(得分:1)
您必须重新解释addrStruct
到sockaddr_in
的演员表。
在您的情况下,代码将如下所示:
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;
但我建议您使用getaddrinfo(3)
代替您的方法。