int print_socket_info(int sock_fd, struct sockaddr_in *sin, short protocol){
char dbg[INET_ADDRSTRLEN];
char *famstr;
inet_ntop(protocol, &(sin->sin_addr), dbg, INET_ADDRSTRLEN);
printf("============ SOCKET INFORMATION =============\n");
printf("!** socket: %d\n", sock_fd);
printf("!** info->ai_addr: sockaddr_in(\n");
famstr = fam2str(sin->sin_family);
printf("!** sin_family: %s\n", famstr);
printf("!** sin_port: %d\n", ntohs(sin->sin_port));
printf("!** sin_addr: in_addr( s_addr : '%s' )\n", dbg);
printf("!**)\n");
printf("=============================================\n");
return 1;
}
char *fam2str(int fam){
switch (fam){
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "(UNKNOWN)";
}
return "(UNKNOWN)";
}
如果我传入了hint.ai_addr(忽略信息-> ...这是字符串的一部分),则如下所示:
print_socket_info(sock, (struct sockaddr_in *)hint.ai_addr, protocol);
...然后我打印出以下内容...
============ SOCKET INFORMATION =============
!** socket: 3
!** info->ai_addr: sockaddr_in(
!** sin_family: AF_INET6
!** sin_port: 8081
!** sin_addr: in_addr( s_addr : '::1' )
!**)
=============================================
...信息已正确打印。接下来,我调用该函数:
res = getaddrinfo(target_host, target_port, &hint, &info);
到目前为止,我没有任何错误。现在,我遍历链表:
struct addrinfo *rp;
for (rp = info; rp != NULL; rp = rp->ai_next){
printf("==> Another element.\n");
print_socket_info(sock, (struct sockaddr_in *) rp->ai_addr, protocol);
}
...我只打印出一个元素:
============ SOCKET INFORMATION =============
!** socket: 3
!** info->ai_addr: sockaddr_in(
!** sin_family: AF_INET6
!** sin_port: 8081
!** sin_addr: in_addr( s_addr : '::' )
!**)
=============================================
...这当然会给bind()造成严重破坏。为什么地址缩短了?
奇怪的是:如果我传入127.0.0.1并使用AF_INET4,则该地址将在整个程序中维护(尽管我只得到一个结果,但是绑定仍然失败)。
有任何想法吗?提前致谢。
最佳答案
您的print_socket_info
函数是错误的。 If采用struct sockaddr_in *
(IPv4套接字地址结构),但它同时支持IPv4和IPv6。
您必须声明print socket_info
以采用通用struct sockaddr *
(任何类型的套接字地址)。为了达到良好的效果:将sin
参数重命名为sa
以表明它属于通用类型,而不是struct sockaddr_in
类型。然后,在函数内部,检查sin->sin_family
以找出实际的家族,然后继续将sin
强制转换为struct sockaddr_in *
或struct sockaddr_in6
。
现有功能中发生的事情是,您只是在整个功能中将其视为struct sockaddr_in *
并具有以下结果(至少在Linux上如此):
检查sin_family
是可以的,因为对于所有类型的sockaddr,无论是struct sockaddr_in
还是struct sockaddr_in6
甚至对于UNIX域套接字的struct sockaddr_un
,还是对于其他所有晦涩的地址族也是如此。
您很幸运在检查sin_port
时摆脱了它,因为sin_port
中的struct sockaddr_in
恰好位于与sin6_port
中的struct sockaddr_in6
相同的结构偏移处。
如果对于sin_addr
不起作用,因为对于IPv4,sin_addr
在结构中的sin_port
之后,但是对于IPv6,在该位置找到了其他字段(即sin6_flowinfo
)。 sin6_addr
在其他地方。print_socket_info
的另一件事是您的字符串缓冲区仅具有足够的空间用于IPv4地址字符串,因为它声明的长度INET_ADDRSTRLEN
对于IPv6来说太短(对于IPv6,您需要INET6_ADDRSTRLEN
。不需要使用参数protocol
该信息已嵌入sockaddr中。
int print_socket_info(int sock_fd, struct sockaddr *sa){
char dbg[INET6_ADDRSTRLEN]; /* the larger of the two sizes */
char *famstr;
unsigned short port;
switch (sa->sa_family):
case AF_INET4:
inet_ntop(AF_INET4, &(((struct sockaddr_in *)sa)->sin_addr), dbg, sizeof(dbg));
port = ((struct sockaddr_in *)sa)->sin_port;
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), dbg, sizeof(dbg));
port = ((struct sockaddr_in6 *)sa)->sin6_port;
break;
default:
strcpy(dst, "UNKNOWN");
port = 0;
}
printf("============ SOCKET INFORMATION =============\n");
printf("!** socket: %d\n", sock_fd);
printf("!** info->ai_addr: sockaddr_in(\n");
famstr = fam2str(sa->sa_family);
printf("!** sa_family: %s\n", famstr);
printf("!** sin[6]_port: %d\n", ntohs(port));
printf("!** sin[6]_addr: in_addr( s_addr : '%s' )\n", dbg);
printf("!**)\n");
printf("=============================================\n");
return 1;
}