适应IP6与IP4下的地址解析[转]


转载
为了适应IP6 中的地址,对于名称的解析可以使用 新的函数 getnameinfo , getaddrinfo , WSAAddressToString , WSAStringToAddress 四个函数 下面分别给出
他们的定义和示例代码。
注意: 使用上述函数需要的头文件 winsock2.h ws2tcpip.h 需要与 Ws2_32.lib 进行连接 , 并且 winsock2.h 必须放在 ws2tcpip.h 之前,因为 ws2tcpip.h 的定义很多依靠的是 winsock2.h 文件.
(1) 将地址转换成可读字符串
INT WSAAddressToString(
LPSOCKADDR lpsaAddress,//地址
DWORD dwAddressLength,//地址长度
LPWSAPROTOCOL_INFO lpProtocolInfo,//使用该协议类解释地址信息,可以为NULL使用地址支持的第一个协议
LPTSTR lpszAddressString, //用于接收转换后的字符串缓冲区
LPDWORD lpdwAddressStringLength //传入字符串缓冲区的长度,返回转换后地址字符串的长度
);
示例代码
char str[200];
DWORD leng=200;
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5500);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
WSAAddressToString((LPSOCKADDR)&addr,sizeof(SOCKADDR),NULL,str,&leng);
//str 返回为 : 127.0.0.1:5500 冒号后面的为端口号,当没有设置端口号码时将不显示
struct addrinfo hints,*result;
int rc;
memset(&hints,0,sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo("127.0.0.1","",&hints,&result);
WSAAddressToString(result->ai_addr,sizeof(sockaddr),NULL,str,&leng);
//str 返回为 : 127.0.0.1
(2) 将字符串转换成一个地址
INT WSAStringToAddress(
LPTSTR AddressString, 以空字符结尾的地址字符串
INT AddressFamily, 地址字符串所属的地址类型 AF_INET / AF_INET6
LPWSAPROTOCOL_INFO lpProtocolInfo, //指定的转换协议信息
LPSOCKADDR lpAddress, 用于存放转换后的地址空间
LPINT lpAddressLength 传入地址空间到缓冲长度,返回实际转换后的长度
);
注意: 当 SOCKADDR_IN 结果的 地址传入时没有设置地址所属地类型 AF_INET 将产生错误.
示例代码
//注意这里的地址的字符串需要加上端口号,才能适用于socket中
INT len = sizeof(addrinfo);
struct addrinfo addr2;
strcpy(str,"127.0.0.1:5500");//冒号后面的为端口号
if(WSAStringToAddress(str,AF_INET,NULL,(LPSOCKADDR)&addr2,&len)!=0)
cout<<"WSAStringToAddress failed!"<<endl;
memset(str,0,200);
WSAAddressToString((LPSOCKADDR)&addr2,sizeof(addrinfo),NULL,str,&leng);
cout<<"WSAAddressToStirng - 2 : "<<str<<endl;

(2)使用getaddrinfo和getnameinfo获得本地地址并在IP框中显示
上述内容参考自:《Windows 网络编程(第二版)》
(3) 使用地址解析
int getaddrinfo(
const char* nodename, //主机名或文字地址
const char* servname, //包含一个端口号或服务名(FTP)
const struct addrinfo* hints, //用于传递一些选项,这些选项将影响到名称解析
struct addrinfo** res //返回 addrinfo 结构的一个链表,该结构包含了由字符串名称解析而来的地址
);
这里的hints结构用于告诉函数如何执行解析。
hints 的结构成员说明 , 如果没有 hints 结构传递,函数需要根据零 hints 结构执行,这是 si_family 设置为 AF_UNSPEC.
typedef struct addrinfo {
int ai_flags; //AI_PASSIVE AI_CANONNAME AI_NUMERICHOST
int ai_family; //AF_INET AF_INET6 AF_UNSPEC
int ai_socktype; //SOCK_DGRAM SOCK_STREAM 在servname 有效时使用
int ai_protocol; //指定要求的协议 (如 IPPROTO_TCP) 在servname 有效时使用
size_t ai_addrlen;
char* ai_canonname;
struct sockaddr* ai_addr;
struct addrinfo* ai_next;
} addrinfo;
如果和数解析成功,地址会通过 res 返回,如果名称被解析为多个地址,则返回结果为一个由 ai_next 字段形成的链表.
每个有名称解析而来的地址由 ai_addr 表示,其长度为 ai_addrlen. 可以使用解析得到的参数直接创建套接字.
示例代码
WSADATA data;
WSAStartup(MAKEWORD(2,2),&data);
SOCKET s;
struct addrinfo hints,
*result;
int rc;
memset(&hints,0,sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo("127.0.0.1","",&hints,&result); //没有使用服务名称,使用了本地地址
rc=getaddrinfo("chx","5500",&hints,&result);//可以直接指定端口号码
rc=getaddrinfo("","5500",&hints,&result); //不指定主机名称,使用默认的
rc=getaddrinfo("chx","",&hints,&result); //使用计算机名称,来返回指定计算机的地址.
if(rc!=0)
cout<<"getaddrinfo failed!"<<endl;
可以使用 AI_PASSIVE 标志,该标志用于获取能够传递给 bind 函数的地址. Hints 结构应该指名地址族,如果使用 AF_UNSPEC 则可能返回两个地址分别可用于绑定 IP4 / IP6 .需要将 nodename 设置为 NULL , servname 应该指定要绑定的端口可以是 0 .
rc=getaddrinfo("","5500",&hints,&result);
注意在返回地址后需要将 result 中的内存释放,可以使用 freeaddrinfo(result) ;
(4)通过一个已经初始化的地址获取信息 getnameaddr 返回与地址及端口信息对应的主机和服务名
int getnameinfo(
const struct sockaddr* sa,
socklen_t salen,
char* host, 接收主机名称的字符串缓冲区
DWORD hostlen, 主机缓冲区的大小
char* serv, 接收服务或端口的字符缓冲区
DWORD servlen, serv缓冲区的大小
int flags
);
示例代码
char name[100];
char port[100];
DWORD namelen=100,portlen=100;
//其中 result 是执行 getaddrinfo 所得.
if(getnameinfo((LPSOCKADDR)result->ai_addr,result->ai_addrlen,name,namelen,NULL,0,0)==0)
if(getnameinfo((LPSOCKADDR)result->ai_addr,result->ai_addrlen,name,namelen,port,portlen,NI_NUMERICSERV)==0) //如果需要获得端口号需要指定 NI_NUMERICSERV标志,表明将端口信息作为字符串返回,而不会讲端口信息解析成服务,当该端口没有服务时就产生错误,因此需要指明不要解析.
{
cout<<"name = "<<name<<endl;
}
else
cout<<"getnameinfo failed."<<endl;
//下面使用了NI_NUMERICSERV|NI_NUMERICHOST表示将主机和端口都使用数字字符串的形式返回。
可以返回一个本地地址
rc = getnameinfo(result->ai_addr,result->ai_addrlen,cname,100,port,100,NI_NUMERICSERV|NI_NUMERICHOST);

你可能感兴趣的:(适应IP6与IP4下的地址解析[转])