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

Smi*_*yne 1 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;
}
Run Code Online (Sandbox Code Playgroud)

所以我的结果是

lo =1 127.0.0.1
enp2s0 =1 172.20.55.6
172.20.55.6
Run Code Online (Sandbox Code Playgroud)

但是在另一台计算机上,接口名称是不同的。他们得到像波纹管一样的结果,

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)
Run Code Online (Sandbox Code Playgroud)

无论接口名称是什么,我都想获取我的Ip地址。如果接口名称与其他计算机不同,如何获取本地IP地址。无论接口名称是什么,都应该给ip地址。我怎样才能做到这一点?

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

Spe*_*ert 9

这些答案似乎都不够好:要么浏览界面太麻烦,要么需要连接到互联网。

这是一种基于 Justin Randall 的回答的方法。它基本相同,但它连接的是 UDP 套接字而不是 TCP。根据udp(7)connect(3)在未绑定的 UDP 套接字上使用:

将自动分配一个空闲的本地端口 [...] 并将套接字绑定到 INADDR_ANY

此外,与 TCP 套接字相反,UDP 套接字上的 connect(3) 不存在任何网络开销或通信,因为它仅更改有关丢弃哪些数据包以及保留在套接字缓冲区中的数据包的规则。

因此,连接到任何INADDR_LOOPBACK不足以检索已选择绑定套接字的本地地址的IP 地址。

#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 = 1337;   // can be any 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";
    }
}

Run Code Online (Sandbox Code Playgroud)

  • 这意外地与 @JustinRandall 的答案相同,并且没有互联网就无法工作,因为: `loopback.sin_addr.s_addr = INADDR_LOOPBACK;` 应该是 `loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);` 没有 htonl 它大多数主机实际上连接到“1.0.0.127”一个合法的互联网IP。连接到环回仅返回环回地址,正如@LightnessRacesinOrbit所说。 (2认同)
  • @SamBob 所以这似乎是一个完全有效的论点。但是,我记得为什么我首先删除了“htonl()”:正如您所说,使用真正的环回地址不仅连接到它,而且还将套接字绑定到它。实际上,上面的代码发生的情况是,带有 UDP 套接字的 connect() 不会产生任何网络开销,它只会更改哪些数据包被丢弃以及哪些数据包不被丢弃的规则。在“connect()”更改规则之前,如 man connect(3) 中所述,在套接字上执行“bind()”到未使用的本地地址。我将更新答案以使其更清楚。 (2认同)

Jus*_*all 6

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

通过查看网络接口很难可靠地获取本地ip地址。正如您已经发现的那样,网络接口名称对于您在其上运行的每个主机可以是唯一的。为了使事情更加复杂,计算机可能具有多个网络接口,并且每个网络接口可能连接也可能不连接到Internet。

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您的应用程序无论如何都需要互联网访问,则非常优雅,但是,这会使您的解决方案变得不自成体系,这实在令人不快。 (2认同)