0
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 ,那么该地址会在整个程序中保持不变(但我只得到一个结果并且绑定仍然失败)。

有任何想法吗?提前致谢。

4

1 回答 1

1

你的print_socket_info功能是错误的。如果采用struct sockaddr_in *(IPv4 套接字地址结构),但它意味着同时支持 IPv4 和 IPv6。

您必须声明print socket_info采用泛型struct sockaddr *(任何类型的套接字地址)。为了更好地衡量:重命名sin参数以sa表明它是泛型类型,而不是struct sockaddr_in类型。然后,在函数内部,您检查sin->sin_family以找出实际的族是什么,并继续通过强制转换sin为 astruct sockaddr_in *struct sockaddr_in6视情况而定。

您现有函数中发生的事情是,您只是将其视为struct sockaddr_in *整个函数,并具有以下结果(至少在 Linux 上):

  • 检查sin_family是可以的,因为对于所有类型的 sockaddr,无论是或什至对于 UNIX 域套接字或所有其他模糊地址系列的 sockaddr 结构,都可以保证该系列在结构中的偏移struct sockaddr_instruct sockaddr_in6相同struct sockaddr_un
  • 检查时您很幸运能够逃脱它,sin_port因为sin_portinstruct sockaddr_in恰好与 in 位于结构中的相同偏移sin6_portstruct 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;
}
于 2013-01-26T20:05:19.843 回答