网络中的主机需要安装TCP/IP协议或其他协议才能进行通信,网络应用程序有时需要获取主机上或网络有关的信息,这些信息一般存放在数据文件中。如Unix系统中,主机名存放在/etc/hosts文件中,网络信息则存放在etc/networks文件中,因此这类获取主机上与网络有关的信息的函数也叫数据库函数。
与网络有关的信息主要分为4大类:
主机信息:函数名一般以gethostby开头,在程序中较多使用;
网络信息:函数名一般以getnetby开头,在程序中较少使用;
协议信息:函数名一般以getprotoby开头,在程序中较少使用;
服务信息:函数名一般以getservby开头,在程序中较多使用。
1)获得主机名---gethostname()
gethostname()函数用来获得一台主机的名称信息:
int gethostname(
__out char *name, //一个指向将要存放主机名的缓冲区的指针
__in int namelen //缓冲区的长度
);
如果函数调用成功,则返回0;如果调用失败,则返回SOCKET_ERROR错误信息。可以通过调用WSAGetLastError()来得到一个特定的错误代码,错误代码说明如下:
WSAEFAULT //名字长度参数太小
WSANOTINITIALISED //在应用这个API前,必须成功调用WSAStartup()
WSAENETDOWN //Windows Sockets实现检测到了网络子系统的错误
WSAEINPROGRESS //一个阻塞Windows Sockets操作正在进行
该函数把本地主机名存放入由name参数指定的缓冲区。主机名的形式取决于Windows Sockets系统的实现,它可能是一个简单的主机名,或者是一个域名。不管是哪种形式,该函数返回的名字必定可以在gethostbyname()和WSAAsyncGetHostByName()函数中使用。
2)获得与套接字相连的远程协议地址---getpeername()
getpeername()函数格式如下:
int getpeername(
__in SOCKET s, //一个已经建立连接的套接字描述字
__out struct sockaddr *name, //指向返回的远程协议地址
__inout int *namelen //远程协议地址长度
);
函数调用成功,返回0;调用失败,则返回SOCKET_ERROR错误信息,可以调用WSAGetLastError()函数来获取对该错误的进一步描述,错误代码如下:
WSANOTINITIALISED //在使用此函数前应成功调用WSAStartup()
WSAENETDOWN //Windows套接字实现检测到网络子系统失效
WSAEFAULT //namelen参数不够大
WSAEINPROGRESS //一个阻塞Windows套接口调用正在进行中
WSAENOTCONN //套接口未建立连接
WSAENOTSOCK //描述字不是一个套接口
getpeername()函数用于从套接口s中获取与它绑定的远程协议的地址信息,并将它存放在sockaddr类型的name结构中。它只能用于已经建立连接的套接口。对于数据报类型的套接口来说,它只能返回先前调用connect()函数时使用的对等端信息,在sendto()函数中使用过的对等端信息不能被返回。
3)获得套接口本地协议地址---getsockname()
getsockname()函数的格式如下:
int getsockname(
__in SOCKET s, //标识一个套接口描述字
__out struct sockaddr *name, //指向返回的本地协议地址的指针
__inout int *namelen //本地协议地址长度,当函数调用完成后,它可以返回实际的本地地址长度
);
调用成功返回0,失败则返回SOCKET_ERROR错误信息,可以通过调用WSAGetLastError()函数获取进一步错误描述。错误代码如下:
WSANOTINITIALISED //在使用此API之前应成功调用WSAStartup()
WSAENETDOWN //Windows套接口实现检测到网络子系统失效
WSAEFAULT //namelen参数不够大
WSAEINPROGRESS //一个阻塞的Windows套接口调用正在进行中
WSAENOTSOCK //描述字不是一个套接口
getsockname()函数用于获取一个套接口的协议地址,它用于一个已绑定或已连接套接口。本调用特别适用于如下情况:未调用bind()就调用了connect(),这时唯有getsockname()调用可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。
若一个套接口与INADDR_ANY绑定,即该套接口可以用任意的主机地址,此时除非调用connect()或accept()来连接,否则getsockname()将不会返回主机IP地址的任何信息。
除非套接口被连接,Windows套接口应用程序不应假设IP地址会从INADDR_ANY编程其他地址。这是因为对于一个有多个IP地址的主机来说,除非套接口被连接,否则该套接口所用的IP地址是不可知的(即不能确定是哪一个)。
4)根据主机名取得主机信息---gethostbyname()或WSAAsyncGetHostByName()
这两个Winsock API函数从主机数据库取回与指定的主机名对应的主机信息。这两个函数均返回一个hostent结构型的变量:
typedef struct hostent {
char FAR *h_name; //正式的主机名
char FAR FAR **h_aliases; //二维字符指针,返回一台主机的所有别名(即别名列表)
short h_addrtype; //主机地址类型,如AF_INET表示IPv4地址
short h_length; //可以返回主机地址的字节数
char FAR FAR **h_addr_list; //返回一台主机的所有IP地址。这个数组中的每个地址都是
//按网络字节顺序返回的。一般情况下,应用程序都采用该数组的第一个地址。
//但是如果返回的地址不止一个,应用程序就会相应地选择一个最恰当的地址,
//而不是一直都用第一个地址
} HOSTENT, *PHOSTENT, FAR *LPHOSTENT;
在Winsock1中提供的gethostbyname()函数格式如下:
struct hostent* FAR gethostbyname(
__in const char *name //一个指向主机名的指针
);
在Winsock1中提供的异步扩展WSAAsyncGetHostByName()函数的格式是:
HANDLE WSAAsyncGetHostByName(
__in HWND hWnd, //一个窗口句柄,表示异步请求完成时,该窗口句柄应该收到一条消息
__in unsigned int wMsg, //当异步请求完成时,将要接收的消息
__in const char *name, //指向主机名的指针
__out char *buf, //接收hostent数据的数据区指针。该数据区必须大于hostent结构的大小,这是因为
//Windows Sockets实现不仅要用该数据区容纳hostent结构,而且hostent结构的成员引用的所有
//数据也要在该区域内。建议用户提供一个MAXGETHOSTSTRUCT字节大小的缓冲区
__in int buflen //buf的大小
);
调用成功,gethostbyname()函数返回hostent结构的指针,调用失败则返回一个空指针,调用WSAGetLastError()得到特定的错误代码:
WSANOTINITIALISED //应用这个API前,必须成功调用WSAStartup()
WSAENETDOWN //Windows Sockets实现检测到了网络子系统的错误
WSAHOST_NOT_FOUND //没有找到授权应答的主机
WSATRY_AGAIN //没有找到非授权主机,或者服务器故障(SERVERFAIL)
WSANO_RECOVERY //无法恢复的错误,如查询格式错误(FORMERR)、拒绝服务(REFUSED)
WSANO_DATA //有效的名字,但没有关于请求类型的数据记录
WSAEINPROGRESS //一个阻塞的Windows Sockets操作正在进行
WSAEINTR //阻塞调用被WSACancelBlockingCall()取消了
WSAAsyncGetHostByName()函数是gethostbyname()函数的Windows异步扩展函数。所谓异步,指的是Windows Sockets的实现启动该操作后立即回到调用方,并传回一个异步任务句柄,应用程序可以用它来标识该操作。
当异步操作完成时,如果成功则将主机名和地址信息拷贝到buf缓冲区中,同时向句柄为hWnd的应用程序窗口发送一条消息(wMsg)。wParam参数包含了初次函数调用时返回的异步任务句柄,lParam的高16位包含着错误代码。
错误代码为0说明异步操作成功,在成功完成的情况下,提供给初始函数调用的缓冲区中包含额一个hostent结构。
错误代码为WSAENOBUFS,则说明初始调用时有buflen指出的缓冲区大小太小;在这种情况下,lParam的低16位提供所有信息所需的缓冲区大小。
错误代码和缓冲区大小应使用WSAGETASYNCERROR和WSAGETASYNCBUFLEN宏从lParam中取出。这两个宏定义为:
#define WSAGETASYNCBUFLEN(lParam) LOWORD(lParam)
#define WSAGETASYNCERROR(lParam) HIWORD(lParam)
使用这些宏可以最大限度的提供程序源代码的可移植性。
函数的返回值指出异步操作是否成功启动,注意它并不隐含操作本身成功与否。若操作成功,则WSAAsyncGetHostByName()返回一个HANDLE类型的非0值,它用来标识该异步请求任务的句柄。通过该句柄使用WSACancelAsyncRequest()函数可取消该操作,也可通过检查wParam消息参数,以匹配异步操作和完成消息。如果异步操作不能启动,WSAAsyncGetHostByName()返回一个0值,使用WSAGetLastError()得到下面错误代码:
WSANOTINITIALISED //在使用本API前必须进行一次成功的WSAStartup()调用
WSAENETDOWN // Windows Sockets实现检测到了网络子系统的错误
WSAEINPROGRESS //一个阻塞的Windows Sockets操作正在进行
WSAEWOULDBLOCK //本异步操作此时由于Windows Sockets实现的资源或其他限制的制约而无法调度
在应用程序的窗口收到消息时可能会设置下列错误代码,它们可以通过WSAGETASYNCERROR宏从应答的消息lParam中取出:
WSAENETDOWN // Windows Sockets实现检测到了网络子系统的错误
WSAENOBUFS //可用的缓冲区空间不足或没有
WSAHOST_NOT_FOUND //未找到授权应答主机
WSATRY_AGAIN //没有找到非授权主机,或者服务器故障(SERVERFAIL)
WSANO_RECOVERY //不可恢复性错误
WSANO_DATA //无请求类型的数据记录
实例代码如下:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char **argv)
{
//-----------------------------------------
// Declare and initialize variables
WSADATA wsaData;
int iResult;
DWORD dwError;
int i = 0;
struct hostent *remoteHost;
char *host_name;
struct in_addr addr;
char **pAlias;
// Validate the parameters
if (argc != 2) {
printf("usage: %s hostname/n", argv[0]);
printf(" to return the IP addresses for the host/n");
printf(" %s www.contoso.com/n", argv[0]);
printf(" or/n");
printf(" %s IPv4string/n", argv[0]);
printf(" to return an IPv4 binary address for an IPv4string/n");
printf(" %s 127.0.0.1/n", argv[0]);
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d/n", iResult);
return 1;
}
host_name = argv[1];
printf("Calling gethostbyname with %s/n", host_name);
remoteHost = gethostbyname(host_name);
if (remoteHost == NULL) {
dwError = WSAGetLastError();
if (dwError != 0) {
if (dwError == WSAHOST_NOT_FOUND) {
printf("Host not found/n");
return 1;
} else if (dwError == WSANO_DATA) {
printf("No data record found/n");
return 1;
} else {
printf("Function failed with error: %ld/n", dwError);
return 1;
}
}
} else {
printf("Function returned:/n");
printf("/tOfficial name: %s/n", remoteHost->h_name);
for (pAlias = remoteHost->h_aliases; *pAlias != 0; pAlias++) {
printf("/tAlternate name #%d: %s/n", ++i, *pAlias);
}
printf("/tAddress type: ");
switch (remoteHost->h_addrtype) {
case AF_INET:
printf("AF_INET/n");
break;
case AF_NETBIOS:
printf("AF_NETBIOS/n");
break;
default:
printf(" %d/n", remoteHost->h_addrtype);
break;
}
printf("/tAddress length: %d/n", remoteHost->h_length);
i = 0;
if (remoteHost->h_addrtype == AF_INET)
{
while (remoteHost->h_addr_list[i] != 0) {
addr.s_addr = *(u_long *) remoteHost->h_addr_list[i++];
printf("/tIP Address #%d: %s/n", i, inet_ntoa(addr));
}
}
else if (remoteHost->h_addrtype == AF_NETBIOS)
{
printf("NETBIOS address was returned/n");
}
}
return 0;
}
5)根据主机地址取得主机信息---gethostbyaddr()或WSAAsyncGetHostByAddr()
这两个函数可以根据主机的IP地址取得主机名和主机地址等信息。
在Winsock1中提供的gethostbyaddr()函数的格式是:
struct hostent* FAR gethostbyaddr(
__in const char *addr, //指向网络字节顺序地址的指针
__in int len, //地址的长度,如果是IPv4类型的地址,则该值是4
__in int type //地址类型,如AF_INET
);
在Winsock1中提供的异步扩展WSAAsyncGetHostByAddr()函数的格式是:
HANDLE WSAAsyncGetHostByAddr(
__in HWND hWnd, //窗口句柄,表示当异步请求完成时,该窗口句柄应该收到一个消息
__in unsigned int wMsg, //当异步请求完成后,将要接收的消息
__in const char *addr, //指向网络字节顺序地址的指针
__in int len, //地址的长度,如果是IPv4类型的地址,则该值是4
__in int type, //地址类型,如AF_INET
__out char *buf, //接收hostent数据的数据区指针
__in int buflen //buf的大小
);
返回信息同4)。
实例如下:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
int main(int argc, char **argv)
{
//-----------------------------------------
// Declare and initialize variables
WSADATA wsaData;
int iResult;
DWORD dwError;
int i = 0;
int bIpv6 = 0;
struct hostent *remoteHost;
char *host_addr;
struct in_addr addr = { 0 };
IN6_ADDR addr6;
char **pAlias;
// Validate the parameters
if (argc < 2) {
printf("usage: %s 4 ipv4address/n", argv[0]);
printf(" or/n");
printf("usage: %s 6 ipv6address/n", argv[0]);
printf(" to return the hostname/n");
printf(" %s 4 127.0.0.1/n", argv[0]);
printf(" %s 6 0::1/n", argv[0]);
return 1;
}
// Validate parameters
if (atoi(argv[1]) == 4)
bIpv6 = 0;
else if (atoi(argv[1]) == 6)
bIpv6 = 1;
else {
printf("usage: %s 4 ipv4address/n", argv[0]);
printf(" or/n");
printf("usage: %s 6 ipv6address/n", argv[0]);
printf(" to return the hostname/n");
printf(" %s 4 127.0.0.1/n", argv[0]);
printf(" %s 6 0::1/n", argv[0]);
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d/n", iResult);
return 1;
}
host_addr = argv[2];
printf("Calling gethostbyaddr with %s/n", host_addr);
if (bIpv6 == 1) {
{
iResult = inet_pton(AF_INET6, host_addr, &addr6);
if (iResult == 0) {
printf("The IPv6 address entered must be a legal address/n");
return 1;
} else
remoteHost = gethostbyaddr((char *) &addr6, 16, AF_INET6);
}
} else {
addr.s_addr = inet_addr(host_addr);
if (addr.s_addr == INADDR_NONE) {
printf("The IPv4 address entered must be a legal address/n");
return 1;
} else
remoteHost = gethostbyaddr((char *) &addr, 4, AF_INET);
}
if (remoteHost == NULL) {
dwError = WSAGetLastError();
if (dwError != 0) {
if (dwError == WSAHOST_NOT_FOUND) {
printf("Host not found/n");
return 1;
} else if (dwError == WSANO_DATA) {
printf("No data record found/n");
return 1;
} else {
printf("Function failed with error: %ld/n", dwError);
return 1;
}
}
} else {
printf("Function returned:/n");
printf("/tOfficial name: %s/n", remoteHost->h_name);
for (pAlias = remoteHost->h_aliases; *pAlias != 0; pAlias++) {
printf("/tAlternate name #%d: %s/n", ++i, *pAlias);
}
printf("/tAddress type: ");
switch (remoteHost->h_addrtype) {
case AF_INET:
printf("AF_INET/n");
break;
case AF_INET6:
printf("AF_INET6/n");
break;
case AF_NETBIOS:
printf("AF_NETBIOS/n");
break;
default:
printf(" %d/n", remoteHost->h_addrtype);
break;
}
printf("/tAddress length: %d/n", remoteHost->h_length);
if (remoteHost->h_addrtype == AF_INET) {
while (remoteHost->h_addr_list[i] != 0) {
addr.s_addr = *(u_long *) remoteHost->h_addr_list[i++];
printf("/tIPv4 Address #%d: %s/n", i, inet_ntoa(addr));
}
} else if (remoteHost->h_addrtype == AF_INET6)
printf("/tRemotehost is an IPv6 address/n");
}
return 0;
}
6)根据协议名取得主机协议信息---getprotobyname()或WSAAsyncGetProtoByName()
函数getprotobyname()和WSAAsyncGetProtoByName()可以根据协议名称返回对应的相关协议信息,它们都要使用一个与协议有关的结构:
typedef struct protoent {
char FAR *p_name; //正式协议名
char FAR FAR **p_aliases; //二维字符指针,返回一个协议的所有别名
short p_proto; //以主机字节顺序排列的协议号
} protoent;
在Winsock1中提供的getprotobyname()函数格式是:
struct PROTOENT* FAR getprotobyname(
__in const char *name //指向协议名的指针
);
在Winsock1中提供的异步扩展WSAAsyncGetProtoByName()函数格式是:
HANDLE WSAAsyncGetProtoByName(
__in HWND hWnd, //窗口句柄,表示当异步请求完成时,该窗口句柄应该收到一个消息
__in unsigned int wMsg, //当异步请求完成后,将要接收的消息
__in const char *name, //指向协议名的指针
__out char *buf, //接收protoent数据的缓冲区指针
__out int buflen //buf的大小
);
返回信息类似WSAAsyncGetHostByName()。
7)根据协议号取得主机协议信息---getprotobynumber()或WSAAsyncGetProtoByNumber()
这两个函数返回对应于给定协议号的相关协议信息。
在Winsock1中提供的getprotobynumber()函数的格式是:
struct PROTOENT* FAR getprotobynumber(
__in int number //一个以主机字节顺序排列的协议号
);
在Winsock1中提供的异步扩展WSAAsyncGetProtoByNumber()函数的格式是:
HANDLE WSAAsyncGetProtoByNumber(
__in HWND hWnd,
__in unsigned int wMsg,
__in int number, //一个以主机字节顺序排列的协议号
__out char *buf,
__in int buflen
);
8)根据服务名取得相关服务信息---getservbyname()或WSAAsyncGetServByName()
这两个函数用于返回对应于给定服务名和协议名的相关服务信息,它们都用到如下的结构:
typedef struct servent {
char FAR *s_name; //正式的服务名
char FAR FAR **s_aliases; //所有其他的可选服务别名
short s_port; //连接该服务时需要用到的端口号,返回的端口号是以网络字节顺序排列的
char FAR *s_proto; //连接该服务时用到的协议名
} SERVENT, *PSERVENT, FAR *LPSERVENT;
在Winsock1中提供的getservbyname()函数格式是:
struct servent* FAR getservbyname(
__in const char *name, //一个指向服务名的指针
__in const char *proto //指向协议名的指针(可选)。如果这个指针为空,getservbyname()返回
//第一个name与s_name或者某一个s_aliases匹配的服务条目。否则,getservbyname()对name
//和proto都进行匹配
);
在Winsock1中提供的异步扩展WSAAsyncGetServByName()函数的格式是:
HANDLE WSAAsyncGetServByName(
__in HWND hWnd,
__in unsigned int wMsg,
__in const char *name,
__in const char *proto,
__out char *buf,
__in int buflen
);
9)根据端口号取得相关服务信息---getservbyport()或WSAAsyncGetServByPort()
这两个函数用于返回对应于给定端口号和协议名的相关服务信息。
在Winsock1中提供的getservbyport()函数的格式是:
struct servent* FAR getservbyport(
__in int port, //给定的端口号,以网络字节顺序排列
__in const char *proto //指向协议名的指针(可选)。如果这个指针为空,getservbyport()
//返回第一个port与s_port匹配的服务条目;否则,getservbyport()对port和proto都进行匹配
);
在Winsock1提供的异步扩展WSAAsyncGetServByPort()函数的格式是:
HANDLE WSAAsyncGetServByPort(
__in HWND hWnd,
__in unsigned int wMsg,
__in int port,
__in const char *proto,
__out char *buf,
__in int buflen
);