使用Boost.Asio获取本地IP地址

时间:2010-04-20 10:37:01

标签: c++ ip-address boost-asio

我目前正在寻找一种获取本地IP地址的便携方式。因为我正在使用Boost,我认为使用Boost.Asio来完成这项任务是个好主意。

网上有几个例子可以解决问题。例子:

Official Boost.Asio Documentation

Some Asian Page

我只是略微修改了两个代码。 Boost.Doc上的代码更改为不解析“www.boost.org”,而是“localhost”或我的主机名。为了获取主机名,我使用了boost :: asio :: ip :: host_name()或直接输入为字符串。

此外,我编写了自己的代码,这些代码是上述示例和我从Boost文档和其他示例中收集的(小)知识的合并。

所有来源都有效,但他们只返回以下IP:
127.0.1.1(这不是一个错字,最后是.1.1)
我使用GCC 4.4.1在Ubuntu 9.10上运行并编译代码

一位同事在他的机器上尝试了同样的代码并得到了 127.0.0.2(也不是错字......)
他使用GCC 4.4.1编译并运行Suse 11.0(我不是100%肯定)

我不知道是否可以更改localhost(127.0.0.1),但我知道我或我的同事都没有这样做。 ifconfig表示loopback使用127.0.0.1。 ifconfig还找到我要搜索的公共IP(在我的情况下是141.200.182.30,子网是255.255.0.0)

这是一个Linux问题,代码不像我想象的那么便携吗?我是否需要更改其他内容或Boost.Asio根本不能解决我的问题?

我知道在Stackoverflow和其他页面上有类似主题的问题很多,但我找不到在我的情况下有用的信息。如果你有了有用的链接,那么如果你能指出我就好了。

PS: 以下是我在Boost.Doc中使用的修改代码:

#include <boost/asio.hpp>
using boost::asio::ip::tcp;    

boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(boost::asio::ip::host_name(), "");
tcp::resolver::iterator iter = resolver.resolve(query);
tcp::resolver::iterator end; // End marker.
while (iter != end)
{
    tcp::endpoint ep = *iter++;
    std::cout << ep << std::endl;
}

5 个答案:

答案 0 :(得分:7)

这是我从python网络编程(google)中学到的一个技巧,可以找出我机器的ip地址。这只适用于您有互联网连接并且可以连接到google.com并且确实给我家用机器的192.168.x.x私人地址。

try {
    boost::asio::io_service netService;
    udp::resolver   resolver(netService);
    udp::resolver::query query(udp::v4(), "google.com", "");
    udp::resolver::iterator endpoints = resolver.resolve(query);
    udp::endpoint ep = *endpoints;
    udp::socket socket(netService);
    socket.connect(ep);
    boost::asio::ip::address addr = socket.local_endpoint().address();
    std::cout << "My IP according to google is: " << addr.to_string() << std::endl;
 } catch (std::exception& e){
    std::cerr << "Could not deal with socket. Exception: " << e.what() << std::endl;

 }

答案 1 :(得分:5)

您可以使用发布的代码找到“您的”地址。但是......它变得复杂了。可能有多个NIC,可能有LAN和WAN地址,有线和无线,环回...在我的桌面上我有一个网卡,但两个ips来自我的局域网上的两个差分DHCP服务器......

我发现最好让用户提供绑定到的IP作为命令行参数。是的,这是一个便携式解决方案! : - )

答案 2 :(得分:4)

如果您编辑/ etc / hosts文件(这只是* nix,也可能适用于Windows ...我不确定)您可以更正此问题。

在hosts文件中你会找到类似的东西:(这是Ubuntu,请注意1.1)

127.0.0.1 localhost
127.0.1.1 yourPcName.yourNetwork.tld

如果您将此文件更改为

127.0.0.1 localhost
127.0.1.1 yourPcName.yourNetwork.tld
your.real.ip.here yourPcName

然后主机名应该正确解析。

测试正确分辨率的一种方法是使用“hostname -i”命令,它应该在更改主机之前错误地打印您的IP地址,然后再正确地打印。

当然这对动态IP来说是一个糟糕的解决方案......呃。

答案 3 :(得分:2)

跨平台,但仅由于#ifdef _WIN32 … #else

boost::asio::ip::address_v6 sinaddr_to_asio(sockaddr_in6 *addr) {
    boost::asio::ip::address_v6::bytes_type buf;
    memcpy(buf.data(), addr->sin6_addr.s6_addr, sizeof(addr->sin6_addr));
    return boost::asio::ip::make_address_v6(buf, addr->sin6_scope_id);
}

#if defined(_WIN32)
#undef UNICODE
#include <winsock2.h>
// Headers that need to be included after winsock2.h:
#include <iphlpapi.h>
#include <ws2ipdef.h>

typedef IP_ADAPTER_UNICAST_ADDRESS_LH Addr;
typedef IP_ADAPTER_ADDRESSES *AddrList;

std::vector<boost::asio::ip::address> get_local_interfaces() {
    // It's a windows machine, we assume it has 512KB free memory
    DWORD outBufLen = 1 << 19;
    AddrList ifaddrs = (AddrList) new char[outBufLen];

    std::vector<boost::asio::ip::address> res;

    ULONG err = GetAdaptersAddresses(AF_UNSPEC,
        GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, ifaddrs,
        &outBufLen);

    if (err == NO_ERROR) {
        for (AddrList addr = ifaddrs; addr != 0; addr = addr->Next) {
            if (addr->OperStatus != IfOperStatusUp) continue;
            // if (addr->NoMulticast) continue;

            // Find the first IPv4 address
            if (addr->Ipv4Enabled) {
                for (Addr *uaddr = addr->FirstUnicastAddress; uaddr != 0; uaddr = uaddr->Next) {
                    if (uaddr->Address.lpSockaddr->sa_family != AF_INET) continue;
                    res.push_back(boost::asio::ip::make_address_v4(ntohl(reinterpret_cast<sockaddr_in *>(addr->ifa_addr)->sin_addr.s_addr)));
                }
            }

            if (addr->Ipv6Enabled) {
                for (Addr *uaddr = addr->FirstUnicastAddress; uaddr != 0; uaddr = uaddr->Next) {
                    if (uaddr->Address.lpSockaddr->sa_family != AF_INET6) continue;
                    res.push_back(sinaddr_to_asio(reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr)));
                }
            }
        }
    } else {

    }
    delete[]((char *)ifaddrs);
    return res;
}
#elif defined(__APPLE__) || defined(__linux__)
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/types.h>

std::vector<boost::asio::ip::address> get_local_interfaces() {
    std::vector<boost::asio::ip::address> res;
    ifaddrs *ifs;
    if (getifaddrs(&ifs)) {
        return res;
    }
    for (auto addr = ifs; addr != nullptr; addr = addr->ifa_next) {
        // No address? Skip.
        if (addr->ifa_addr == nullptr) continue;

        // Interface isn't active? Skip.
        if (!(addr->ifa_flags & IFF_UP)) continue;

        if(addr->ifa_addr->sa_family == AF_INET) {
            res.push_back(boost::asio::ip::make_address_v4(ntohl(
                reinterpret_cast<sockaddr_in *>(addr->ifa_addr)->sin_addr.s_addr)));
        } else if(addr->ifa_addr->sa_family == AF_INET6) {
            res.push_back(sinaddr_to_asio(reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr)));
        } else continue;
    }
    freeifaddrs(ifs);
    return res;
}
#else
#error "..."
#endif

答案 4 :(得分:0)

假设您有一张网卡/一个本地IP地址:

#include <boost/asio.hpp>
namespace ip = boost::asio::ip;

std::string getAddress()
{
    boost::asio::io_service ioService;
    ip::tcp::resolver resolver(ioService);

    return resolver.resolve(ip::host_name(), "")->endpoint().address().to_string();
}