Visual C++ 2017网络编程实战
上QQ阅读APP看书,第一时间看更新

2.1 获取本地计算机的名称和IP

网络中的主机通常有一个主机名称和一个或多个IP地址。有了这些标识,其他主机就能找到我们,就能和我们通信。

2.1.1 gethostname函数

gethostname函数用来检索本地计算机的标准主机名,函数声明如下:

    int gethostname(char *name,  int  namelen);

其中,参数name指向接收本地主机名的缓冲区的指针;namelen表示name所指缓冲区的长度,以字节为单位。如果没有出现错误,那么函数返回零;否则,它将返回SOCKET_ERROR,可以通过调用WSAGetLastError来检索特定的错误代码。

例如,我们可以利用下面的代码来获取本地计算机的名称:

    char szHostName[128];
    char szT[20];
    if( gethostname(szHostName, 128) == 0 )
      puts("本地计算机名称是:%s", szHostName);

2.1.2 gethostbyname函数

gethostbyname函数从主机数据库中检索与主机名对应的主机信息,比如IP地址等,函数声明如下:

    hostent * gethostbyname(  const char *name);

其中,参数name是本地计算机的名称,可以用gethostname获得。如果没有出现错误,就将返回指向hostent结构的指针;否则,将返回一个空指针,并且可以通过调用WSAGetLastError来检索特定的错误号,比如错误号是WSANOTINITIALISED,表示没有预先成功调用WSAStartup函数。

hostent是一个结构体,定义如下:

    typedef struct hostent {
      char  *h_name;
      char  **h_aliases;
      short  h_addrtype;
      short  h_length;
      char  **h_addr_list;
    } HOSTENT, *PHOSTENT, *LPHOSTENT;

其中,h_name表示主机的正式名称。h_aliases指向以NULL结尾的主机别名数组;h_addrtype返回地址类型;h_length表示ip地址的长度,ipv4对应4个字节;一般主机可以有多个ip地址,比如www.163.com就有121.14.228.43和121.11.151.72两个ip,h_addr_list就用来保存多个ip地址。

在Internet环境下为AF-INET;h_length表示地址的字节长度;h_addr_list指向一个以NULL结尾的数组,包含该主机的所有地址。

例如,下面的代码可以获得本机所有的IP地址:

    struct hostent * pHost;
    int i;
    pHost = gethostbyname(szHostName);
    for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
    {
         char str[100];
         char addr[20];
         int j;
         LPCSTR psz=inet_ntoa (*(struct in_addr *)pHost->h_addr_list[i]);
         m_IPAddr.AddString(psz);
    }

2.1.3 inet_ntoa函数

inet_ntoa该函数将一个十进制网络字节序转换为点分十进制IP格式的字符串,函数声明如下:

    char*inet_ntoa(struct  in_addr  in);

其中,参数in表示Internet主机地址的结构,结构体in_addr在第7章中会介绍。如果函数正确,就返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区;如果错误,就返回NULL。

下面我们看一个例子,是一个对话框程序,用来获取本机的名称和IP地址。如果大家对于对话框编程不熟悉,可以参考笔者关于VC2017开发的书籍《Visual C++ 2017从入门到精通》。

【例2.1】获取本机名称和IP地址

(1)新建一个对话框工程,工程名是test。

(2)切换到资源视图,打开对话框编辑器,在对话框上添加一个编辑框、一个列表框(List Box)和一个按钮。其中,编辑框用来显示本机名称,列表框用来显示本机的IP地址。设置按钮的标题是“查询”。为编辑添加控件类型变量m_HostName,为列表框添加控件变量m_IPAddr。

(3)设置工程属性。打开工程属性对话框,设置工程为多字节字符工程,如图2-1所示。

图2-1

展开“C/C++”→“Preprocessor”,在右边第一行“Preprocessor Definitions”旁的开头添加一个宏:

    _WINSOCK_DEPRECATED_NO_WARNINGS;

注意有个分号。有了这个宏,就可以使用一些传统函数了,而不会出现警告,如图2-2所示。

图2-2

(4)切换类视图,选择CtestApp,对其成员函数InitInstance双击,在InitInstance函数中添加Winsock库初始化代码:

    if (!AfxSocketInit())
    {
        AfxMessageBox("AfxSocketInit failed");
        return FALSE;
    }

在test.h开头添加头文件包含:

    #include <afxsock.h>

(5)切换到资源视图,在对话框界面中的“查询”按钮上双击,添加事件响应代码:

    void CtestDlg::OnBnClickedButton1()
    {
    // TODO: Add your control notification handler code here
    char szHostName[128];
    char szT[20];

    if (gethostname(szHostName, 128) == 0)//获取本机名称
    {
         // Get host adresses
         m_HostName.SetWindowText(szHostName);//本机名称显示在编辑框中
         struct hostent * pHost;
         int i;
         pHost = gethostbyname(szHostName);//获取本机网络信息
         for (i = 0; pHost != NULL && pHost->h_addr_list[i] != NULL; i++)
         {
              char str[100];
              char addr[20];
              int j;
              LPCSTR psz = inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]);
              m_IPAddr.AddString(psz); //把IP字符串显示在列表框中
         }
    }
    }

在代码中,首先获取了本机名称,然后显示在编辑框中,接着获取本机网络信息,最后显示在列表框中。

(6)保存工程并运行,结果如图2-3所示。

图2-3