首页 > 技术文章 > 网络编程实验1_(9)获取本机的IP地址(C++代码)

cyx-b 2020-03-04 16:31 原文

#define  _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<WinSock2.h>
#include<string>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()
{
    WSADATA wsaData;         // WSADATA结构包含有关Windows套接字实现的信息。
    WORD wVersionRequest;     // wVersionRequested 为 WinSock 规范的版本号,低字节为主版本号,高字节为副版本号(修正版本号)
    int iResult;             // WSAStartup()的返回值,如果为零,则成功启动网络库
    char name[256];          // 指向接收本地主机名的缓冲区的指针。
    PHOSTENT hostinfo;         // PHOSTENT类型,本质上是一个指向hostent的结构体指针


    wVersionRequest = MAKEWORD(2, 2);
    iResult = WSAStartup(wVersionRequest, &wsaData);
    if (0 != iResult) {
        switch (iResult)
        {
            case WSASYSNOTREADY:
                cout << "底层网络子系统还没有准备好进行网络通信。" << endl;
                break;
            case WSAVERNOTSUPPORTED:
                cout << "此特定的Windows Sockets实现不提供所请求的Windows Sockets支持的版本。" << endl;
                break;
            case WSAEINPROGRESS:
                cout << "正在执行阻塞Windows Sockets 1.1操作。" << endl;
                break;
            case WSAEPROCLIM:
                cout << "已达到Windows Sockets实现支持的任务数量的限制。" << endl;
                break;
            case WSAEFAULT:
                cout << "lpWSAData参数不是有效指针。" << endl;
                break;
        
        }
    }

    /*
        确认WinSock DLL支持2.2。注意,如果DLL支持的版本除2.2外大于2.2,它仍将在wversion中返回2.2,因为这是我们请求的版本
    */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion != 2)) {
        cout << "找不到Winsock.dll的可用版本" << endl;
        WSACleanup();
        return 1;
    }
    else
        cout << "发现了Winsock 2.2 DLL" << endl;


    /*
        gethostname中有两个参数,第一个name是指向接收主机名的缓冲区的指针,第二个参数是缓冲区的长度(以字节为单位)
        gethostbyname中的参数是域名字符串或者指向主机名的指针
        LPCSTR是Win32和VC++所使用的一种字符串数据类型。LPCSTR被定义成是一个指向以'\0'结尾的常量字符的指针。
        inet_ntoa函数将(IPv4)Internet网络地址转换为Internet标准虚数点分十进制格式的ASCII字符串。
        in_addr结构表示IPv4 Internet地址。
        hostinfo在开头定义了,它是PHOSTENT类型,本质上是一个指向hostent的结构体指针,所以虽然没把hostent在代码中写出来,但必须弄清楚
        h_addr_list,这个是地址列表,是hostent结构体中最重要的成员变量,通过该成员以整数形式保存域名对应的 IP 地址
        
    */
    string ServAddr;
    if (gethostname(name, sizeof(name)) == 0) {
        if ((hostinfo = gethostbyname(name)) != NULL) {
            //LPCSTR ip = inet_ntoa(*(struct in_addr*) * hostinfo->h_addr_list);
            ServAddr = inet_ntoa(*(struct in_addr*) * hostinfo->h_addr_list);
            //ServAddr = ip;
            cout << "本机(此处作为服务器)的IP 为:" << ServAddr << endl;
            //cout << "本机(此处作为服务器)的IP 为:" << ip << endl;
       //如果将被注释掉的这三行来代替这两行代码的话也是可以的
} } else { switch (gethostname(name, sizeof(name))) { case WSAEFAULT: cout << "name参数是空指针,或者不是用户地址空间的有效部分。如果namelen参数指定的缓冲区大小太小,无法容纳完整的主机名,也会返回此错误。" << endl; break; case WSANOTINITIALISED: cout << "在使用此函数之前,必须进行成功的WSAstartup调用。" << endl; break; case WSAENETDOWN: cout << "网络子系统失败。" << endl; break; case WSAEINPROGRESS: cout << "阻塞Windows套接字1.1调用正在进行,或者服务提供者仍在处理回调函数。" << endl; break; } } WSACleanup(); // 在socket编程中,必须以WSACleanup()结尾 system("pause"); return 0; }

和上一篇网络编程实验1_(8)获得给定域名的IP地址(C++代码)一样,我通常喜欢把详细的解释放在注释中。

但是获得本机的IP地址和获得给定域名的IP地址有何区别呢?

相同之处在于我们使用的获得IP地址的方法都是通过gethostbyname()这个函数实现的。

这个函数的返回值为一个结构体指针,指向的结构体是hostent,可以通过转到定义查看,

 

 

 

我在程序中使用的就是这个结构体中的成员变量h_addr_list,这其实是一个指针数组,

用来存放解析出来的多个IP地址。

 

和获得给定域名IP地址不同的是,获得本机的IP地址用到了gethostname()这个函数。

这个函数有两个参数,第一个参数是指向接收主机名的缓冲区的指针,别被修饰词绕晕了,它是指向缓冲区的指针。

第二个参数是这个缓冲区的大小。

至于这个缓冲区的大小该设置为多少,MSDN上给出的解释是这样的(我翻译过后的):

 

 

 所以我也就索性设置为了256字节。

这个函数如果运行成功了,返回一个零,否则返回一个错误码。

至于错误码的含义,你可以不用搞清楚,至少对于有耐心看我这个菜鸟写的博客的你来说,应该是没有任何意义的。

所以如果你想简单一点,可以把这个啰嗦的switch case去掉,从else开始,直接删掉也是没有任何问题的。

 

这个任务最重要的是理解hostent这个结构体,以及如何将这个结构体的成员变量中得到IP地址,并转换为我们需要的形式。

如果你觉得我讲的不够清楚的,可以看看我之前的博客,或者去网上查一查hostent、以及inet_ntoa()等等。

 

推荐阅读