《ASCE1885的网络编程》---Winsock APIの网络信息获取函数

网络中的主机需要安装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位提供所有信息所需的缓冲区大小。

错误代码和缓冲区大小应使用WSAGETASYNCERRORWSAGETASYNCBUFLEN宏从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()返回

         //第一个names_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()

         //返回第一个ports_port匹配的服务条目;否则,getservbyport()portproto都进行匹配

);

 

Winsock1提供的异步扩展WSAAsyncGetServByPort()函数的格式是:

HANDLE WSAAsyncGetServByPort(

  __in   HWND hWnd,

  __in   unsigned int wMsg,

  __in   int port,

  __in   const char *proto,

  __out  char *buf,

  __in   int buflen

);

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值