在c ++中获取本地IP地址

时间:2018-03-17 10:37:24

标签: c++ ip

我有一个代码来获取本地IP地址。这是我使用的代码。

typedef std::map<string,string> settings_t;

void loadLocalIp (settings_t &ipConfig)
{
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;      

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            string key(ifa->ifa_name);
            string value(addressBuffer);
            cout<<key<<" =1 " <<value<<endl;
            ipConfig.insert(std::pair<string,string>(key, value));

           // printf("'%s': %s\n", ifa->ifa_name, addressBuffer); 
         }
     }
    if (ifAddrStruct!=NULL) 
        freeifaddrs(ifAddrStruct);//remember to free ifAddrStruct
}

int main()
{
    settings_t ipConfig;
    loadLocalIp(ipConfig);
    cout<<ipConfig.at("enp2s0")<<endl;
    return 0;
}

所以我的结果是

lo =1 127.0.0.1
enp2s0 =1 172.20.55.6
172.20.55.6

但在另一台计算机中,界面名称不同。他们得到的结果如下,

lo =1 127.0.0.1
ens32 =1 172.20.55.9
terminate called after throwing an instance of 'std::out_of_range'
  what():  map::at
Aborted (core dumped)

无论接口名称是什么,我想获取我的IP地址。如果接口名称因计算机而异,如何获取本地IP地址。无论接口名称是什么,它都应该给出ip地址。我怎么能这样做?

我的问题是,现在我从这个方法获取本地IP。但无论接口名称是什么,我都应该获得IP。有一件事,我需要找到接口名称并将其应用于我的上述代码(或)是否有任何其他选项来找到没有该接口的IP?

4 个答案:

答案 0 :(得分:4)

  

无论接口名称是什么,我都想获取我的IP地址。

通过查看网络接口很难可靠地获取本地IP地址。正如您已经发现的那样,网络接口名称对于您运行的每个主机都是唯一的。更复杂的是,计算机可能有多个网络接口,每个网络接口可能连接也可能没有连接到互联网。

您无需使用默认界面。更简单的方法是让OS路由表为您解决问题。您可以通过设置到某个外部服务器的套接字连接然后调用getsockname来获取本地地址来完成此操作。此示例使用8.8.8.8处的Google DNS服务器建立套接字连接,但您可以使用您喜欢的任何外部服务器。

#include <iostream>     ///< cout
#include <cstring>      ///< memset
#include <errno.h>      ///< errno
#include <sys/socket.h> ///< socket
#include <netinet/in.h> ///< sockaddr_in
#include <arpa/inet.h>  ///< getsockname
#include <unistd.h>     ///< close

int main()
{
    const char* google_dns_server = "8.8.8.8";
    int dns_port = 53;

    struct sockaddr_in serv;
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    //Socket could not be created
    if(sock < 0)
    {
        std::cout << "Socket error" << std::endl;
    }

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(google_dns_server);
    serv.sin_port = htons(dns_port);

    int err = connect(sock, (const struct sockaddr*)&serv, sizeof(serv));
    if (err < 0)
    {
        std::cout << "Error number: " << errno
            << ". Error message: " << strerror(errno) << std::endl;
    }

    struct sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (struct sockaddr*)&name, &namelen);

    char buffer[80];
    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 80);
    if(p != NULL)
    {
        std::cout << "Local IP address is: " << buffer << std::endl;
    }
    else
    {
        std::cout << "Error number: " << errno
            << ". Error message: " << strerror(errno) << std::endl;
    }

    close(sock);
    return 0;
}

答案 1 :(得分:1)

这些答案似乎都不够好:在界面上行走时遇到太多麻烦,或者需要连接到Internet。

这是一种基于贾斯汀·兰德尔的answer的方法。基本相同,但是连接到环回地址127.0.0.1,因此不需要连接到WWW。

在connect(3)的POSIX man页上:

  

如果套接字尚未绑定到本地地址,则connect()必须将其绑定到一个地址,除非套接字的地址族是AF_UNIX,否则该地址是未使用的本地地址。

因为它不需要连接到远程服务器,所以几乎没有开销。

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

int main(void) {
    int sock = socket(PF_INET, SOCK_DGRAM, 0);
    sockaddr_in loopback;

    if (sock == -1) {
        std::cerr << "Could not socket\n";
        return 1;
    }

    std::memset(&loopback, 0, sizeof(loopback));
    loopback.sin_family = AF_INET;
    loopback.sin_addr.s_addr = INADDR_LOOPBACK;   // using loopback ip address
    loopback.sin_port = htons(9);                 // using debug port

    if (connect(sock, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback)) == -1) {
        close(sock);
        std::cerr << "Could not connect\n";
        return 1;
    }

    socklen_t addrlen = sizeof(loopback);
    if (getsockname(sock, reinterpret_cast<sockaddr*>(&loopback), &addrlen) == -1) {
        close(sock);
        std::cerr << "Could not getsockname\n";
        return 1;
    }

    close(sock);

    char buf[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, &loopback.sin_addr, buf, INET_ADDRSTRLEN) == 0x0) {
        std::cerr << "Could not inet_ntop\n";
        return 1;
    } else {
        std::cout << "Local ip address: " << buf << "\n";
    }
}

答案 2 :(得分:0)

感谢您的解决方案。它工作正常。但是当我搜索解决方案时,我也提出了以下答案。请看一下。这个答案的优点和缺点是什么。

 FILE *f;
    char line[100] , *p , *c;

    f = fopen("/proc/net/route" , "r");

    while(fgets(line , 100 , f))
    {
        p = strtok(line , " \t");
        c = strtok(NULL , " \t");

        if(p!=NULL && c!=NULL)
        {
            if(strcmp(c , "00000000") == 0)
            {
                printf("Default interface is : %s \n" , p);
                break;
            }
        }
    }

    //which family do we require , AF_INET or AF_INET6
    int fm = AF_INET;
    struct ifaddrs *ifaddr, *ifa;
    int family , s;
    char host[NI_MAXHOST];

    if (getifaddrs(&ifaddr) == -1) 
    {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    //Walk through linked list, maintaining head pointer so we can free list later
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 
    {
        if (ifa->ifa_addr == NULL)
        {
            continue;
        }

        family = ifa->ifa_addr->sa_family;

        if(strcmp( ifa->ifa_name , p) == 0)
        {
            if (family == fm) 
            {
                s = getnameinfo( ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6) , host , NI_MAXHOST , NULL , 0 , NI_NUMERICHOST);

                if (s != 0) 
                {
                    printf("getnameinfo() failed: %s\n", gai_strerror(s));
                    exit(EXIT_FAILURE);
                }

                printf("address: %s", host);
            }
            printf("\n");
        }
    }

    freeifaddrs(ifaddr);

    return 0;

答案 3 :(得分:0)

获取本地ip的一种很酷的方法是执行ipconfig命令,将输出保存到一个文件中以进行读取,然后解析数据,以便输出仅显示您的ipv4地址。 可以通过以下方式完成:

std::string GetParsedIPConfigData(std::string Columb)
{
    //Set up command file path and command line command
    std::string APPDATA = getenv("APPDATA");
    std::string path = APPDATA + "\\localipdata.txt";
    std::string cmd =  "ipconfig > " + path;

    //execute ipconfig command and save file to path
    system(cmd.c_str());

    //current line
    std::string line;

    //Line array : Here is all lines saved
    std::string lineArray[500];
    int arrayCount = 0;

    std::ifstream file(path);
    if (file.is_open())
    {
        //Get all lines
        while (std::getline(file, line))
        {
            //Save each line into a element in an array
            lineArray[arrayCount] = line;
            arrayCount++;
        }

        for (int arrayindex = 0; arrayindex <= arrayCount; arrayindex++)
        {
            std::string s = Columb;
            std::string s2 = ":";

            //Search all lines and get pos
            std::size_t i = lineArray[arrayindex].find(s);
            std::size_t i2 = lineArray[arrayindex].find(s2);

            //Found a match for Columb
            if (lineArray[arrayindex].find(s) != std::string::npos)
            {
                //Validate
                if (i != std::string::npos)
                {
                    //Earse Columb name
                    lineArray[arrayindex].erase(i, s.length());

                    //Erase all blanks
                    lineArray[arrayindex].erase(remove_if(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), isspace), lineArray[arrayindex].end());

                    //Found match for ':'
                    if (lineArray[arrayindex].find(s2) != std::string::npos)
                    {
                        //Validate
                        if (i2 != std::string::npos)
                        {
                            //Delete all characters prior to ':'
                            lineArray[arrayindex].erase(0, lineArray[arrayindex].find(":"));
                            lineArray[arrayindex].erase(std::remove(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), ':'), lineArray[arrayindex].end());
                        }
                    }
                    //Return our data
                    return lineArray[arrayindex];
                }
            } 

            //Only go through all lines once
            if (arrayindex == arrayCount)
                break; 
        } 

        //Close file
        file.close();
    }
    //Something went wrong
    return "Invalid";
}

然后像这样调用它:

cout << parser.GetParsedIPConfigData("IPv4 Address") << "\n\n";