【发布时间】:2017-07-24 12:56:13
【问题描述】:
我正在尝试熟悉网络 BSD API。我理解提交给 BSD API 的多字节数据(如地址和端口)必须按网络字节顺序排列,我们应该使用 htons() 和 htonl() 之类的转换函数来做到这一点。这段代码的 sn-p 表明:
#define IP_ADDRESS(a, b, c, d) ((((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) | ((uint32_t)d))
/* Set server port in BSD format */
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(IP_ADDRESS(192,168,1,1));
serverAddr.sin_port = htons(21);
/* Connect to server */
status = connect(H->bsdSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
现在我正在尝试使用 getaddrinfo() 来从服务器名称中检索地址信息。我尝试了以下代码:
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo( "srvdc01", "21", &hints, &addr_list );
if (addr_list != NULL)
{
ctx->fd = (int) socket( addr_list->ai_family, addr_list->ai_socktype,
addr_list->ai_protocol );
if (ctx->fd >= 0)
status = connect( ctx->fd, addr_list->ai_addr, addr_list->ai_addrlen );
}
我原以为 getaddrinfo() 返回的地址会按照网络字节顺序排列,这样就可以直接将其提供给 connect() 函数。不幸的是,这不适用于我正在使用的库,因为生成的连接正在恢复地址字节(我使用 Wireshark 来调查这个问题)。
是否允许安全地使用 getaddrinfo() 返回的地址来提供 socket() 和 connect() 调用?
谢谢,弗兰克
=== 更新 ===
感谢你们的 cmets。我可以再澄清一点。我运行了以下代码 sn-p,结果如下:
struct addrinfo hints, *addr_list, *cur;
struct sockaddr_in addr_in;
memset(&addr_in, 0, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(21);
addr_in.sin_addr.s_addr = htonl(0xC0A80101UL); // 192.168.1.1
myprintf("%08x\n", addr_in.sin_addr.s_addr);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if( getaddrinfo( "srvdc01", "21", &hints, &addr_list ) == 0 )
{
for( cur = addr_list; cur != NULL; cur = cur->ai_next )
{
ctx->fd = (int) socket( cur->ai_family, cur->ai_socktype,
cur->ai_protocol );
if( ctx->fd >= 0 )
{
myprintf("%08x\n", ((struct sockaddr_in *)cur->ai_addr)->sin_addr.s_addr);
}
}
}
freeaddrinfo( addr_list );
第一个 printf 输出(在 htonl 之后)给出 c0a80101,而第二个(在创建套接字之后)给出 0101a8c0。这令人惊讶,因为在我的 little endian 平台上,我会期望 htonl() 调用交换字节以将它们带入网络字节顺序。
查看我们的嵌入式网络库(第三方商业库)的文档后,我们可以发现他们提供的 BSD API 实际上是在其供应商特定网络库 API 之上的(相当)有限的包装器。有一点需要注意的是,他们的库始终使用本机系统字节序,因此他们的 htonl() 函数始终是一个不执行任何操作的虚拟调用。
现在,查看 Martin R.http://pubs.opengroup.org/onlinepubs/009695399/functions/freeaddrinfo.html 提供的链接,我同意他的观点,即 getaddrinfo() 应该返回适合调用 connect() 的地址信息,而这个嵌入式库显然不是这种情况。
【问题讨论】:
-
什么是 IP_ADDRESS?
-
我很确定 getaddrinfo 按网络字节顺序填充 ai_addr 条目,以便它们可以直接与 socket() 和 connect() 一起使用。
-
但是您应该尝试 all 返回的地址,例如beej.us/guide/bgnet/output/html/multipage/getaddrinfoman.html 示例代码。
-
嗯,那个宏是怎么工作的?
-
ipv4 的调试打印应该是
printf("%08x\n", ((struct sockaddr_in *)addr_list->ai_addr)->sin_addr.s_addr);。