写一个小程序需要将hostent结构体中以网络字节序存储的主机ip地址转换成点分十进制表示的IPv4地址。上网查了许多资料发现很多写的有问题,请教了大牛后这里总结一下。
hostent结构体在MSDN中的定义:
typedef struct hostent {
char FAR *h_name;
char FAR FAR **h_aliases;
short h_addrtype;
short h_length;
char FAR FAR **h_addr_list;
} HOSTENT, *PHOSTENT, FAR *LPHOSTENT;
在VS2015的库中的定义:
struct hostent {
char FAR * h_name; /* official name of host */
char FAR * FAR * h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};
注意最后一行是多了一个宏定义的,但是网上很多文章中没提,自己一开始没搞明白h_addr这个字段是哪来的。
in_addr 结构体在MSDN中的定义:
typedef struct in_addr {
union {
struct {
u_char s_b1, s_b2, s_b3, s_b4;
} S_un_b;
struct {
u_short s_w1, s_w2;
} S_un_w;
u_long S_addr;
} S_un;
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
in_addr 结构体在VS2015的库中的定义:
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
在实际使用中需要注意两点,第一如果调用gethostbyname函数,根据VS2015的测试必须先用WSAStartup函数初始化网络库,否则gethostbyname返回的值肯定是NULL。但是网上很多文章是直接调用gethostbyname函数的,不知道他们是怎么能正常获取到返回值的。
其次是获取到hostent结构体后,转换为十进制的IP地址时调用inet_ntoa函数,必须赋值给addr.S_un.S_addr,用下面的方法是编译不了的
struct in_addr *in={h->h_addr}; // 报错:char* 类型的值不能用于初始化in_addr* 类型的实体
printf("%s\n",inet_ntoa(*in));
下面是例子代码:
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char** argv)
{
WORD version = MAKEWORD(2, 2);
WSADATA wd = { 0 };
WSAStartup(version, &wd);
hostent* he;
he = gethostbyname("www.126.com");
if (he == nullptr)
{
printf("gethostbyname error");
return 0;
}
in_addr addr;
addr.S_un.S_addr = *(ULONG*)he->h_addr; //正确的转换方法
printf(" official hostname : %s\n",he->h_name);
printf(" alias: %s\n", he->h_aliases);
printf(" Type: %d\n", he->h_addrtype);
printf(" Length: %d\n", he->h_length);
printf(" address: %s\n", inet_ntoa(addr));
WSACleanup();
return 0;
}