【问题标题】:How to get the external IP address in C++?如何在 C++ 中获取外部 IP 地址?
【发布时间】:2017-01-26 17:37:12
【问题描述】:

如何在 C++ 中查找用户的外部 IP?我需要一种适用于任何系统的方法,而不仅仅是我的。此外,系统可能位于路由器后面,因此 NAT 会发挥作用,使检索外部 IP 变得更加困难。

理想情况下,我希望在不使用任何第三方服务(如 whatsmyip)的情况下执行此操作。但是,我不确定这是否可能。如果我不使用 3rd 方服务,我必须通过路由器,如果禁用 ping,我猜这可能是不可能的(我可能是错的,不太确定)。

如果我要使用像 whatsmyip 这样的第三方服务,我该怎么做?是否有他们公开的 Web 服务?我看过这个链接:http://automation.whatismyip.com/n09230945.asp,但它似乎不起作用。 是否可以通过使用一些 HTTP 方法获取外部 IP 并检索它,或者我是否需要有效地抓取页面以从中获取 IP?

我仅限于使用 Windows API 来完成此操作(没有第 3 方 API)

【问题讨论】:

  • 你没有。我的意思是你不会使用 C++ 获得接口(any 接口)的 IP 地址,因为 C++ 没有任何类型的网络概念。相反,您使用本机平台特定功能来获取接口列表。如果你稍微搜索一下,你会在互联网上找到很多关于如何做到这一点的例子,包括这个网站上的很多例子,试着搜索例如枚举网络接口windows api
  • @JoachimPileborg - 我知道如何枚举网络接口。但是,我特别需要使用 c++ 以编程方式查找外部 IP。我希望有本地 API 可以帮助我完成此任务。
  • 您是指您的计算机“外部”地址,还是路由器?你可以很容易地找到一个,但不是另一个。要么使用whatsmyip.org 之类的东西,要么找到一些直接查询路由器的方法。如果您不想使用第 3 方 http-request 库,请查看 Windows Internet function

标签: c++ http ip-address


【解决方案1】:

这与 C++ 或任何语言无关。
此外,知道路由器的地址对你没有多大好处,你会知道它是 NAT 地址,而不是外部地址,它是也可能您的路由器不是连接到互联网的最后一个路由器。
您必须使用外部实体 - 通常是某种网络服务,该服务将报告与其联系的路由器的 IP 地址。
这里是sample question 有人询问如何在 PHP 中获取客户端 IP 地址。

【讨论】:

  • 我不同意。他的问题是一个可以解决的问题,只要您愿意使用 HTTP GET(到任何其他会告诉您您是外部 IP 地址的网站)。在 Windows C++ 中执行此操作的方式与 C# 或 python 不同。所以我认为有特定于 Windows C++ 的问题和答案很有帮助。感谢 Software_Designer 提供有效的答案。
【解决方案2】:

您可以使用 curl 或 curlpp 之类的库来获取 http://myexternalip.com/raw 的内容。它的响应只是你的外部ip,你可以阅读和使用它。

【讨论】:

  • 这是一个第三方库,我猜。是否有任何 Windows 库可以让我完成同样的事情?
  • 如果您使用的是 Unix,我建议您使用 exec 并执行“wget myexternalip.com/raw”命令将内容下载为 html,然后读取该文件。我不知道是否有一个命令可以在 windows 中下载 html 内容。如果有,您可以从您的应用程序中执行它,然后读取下载的 html 文件。
【解决方案3】:

这是winsock方式。它只是提取嵌入在服务器发送的 HTML 代码中的 IP 地址。

此代码使用http://api.ipify.org/

以下是两个不同的代码。一个用于 VS2012-2015 使用 strcpy_s( ) 和一个用于 Visual C++ 6.0 使用 strcpy( ) ,因为 VS2012-2015 会引发错误消息,提示您使用 strcpy_s( ) 而不是 strcpy( )

Visual Studio 2012-2015 代码。

#include "stdafx.h"
#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <locale>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")


string website_HTML;
locale local;
void get_Website(string url);
char lineBuffer[200][80] = { ' ' };
char buffer[10000];
char ip_address[16];
int i = 0, bufLen = 0, j = 0, lineCount = 0;
int lineIndex = 0, posIndex = 0;

//****************************************************

int main(void){
    cout << "\n\n\n";
    get_Website("api.ipify.org");
    for (size_t i = 0; i<website_HTML.length(); ++i) website_HTML[i] = tolower(website_HTML[i], local);

    istringstream ss(website_HTML);
    string stoken;

    while (getline(ss, stoken, '\n')) {

        //cout <<"-->"<< stoken.c_str() << '\n';

        strcpy_s(lineBuffer[lineIndex], stoken.c_str());
        int dot = 0;
        for (int ii = 0; ii< strlen(lineBuffer[lineIndex]); ii++){

            if (lineBuffer[lineIndex][ii] == '.') dot++;
            if (dot >= 3){
                dot = 0;
                strcpy_s(ip_address, lineBuffer[lineIndex]);
            }
        }

        lineIndex++;
    }
    cout << "Your IP Address is  " << ip_address << " \n\n";


    cout << "\nPress ANY key to close.\n\n";
    cin.ignore(); cin.get();

    return 0;
}

//****************************************************

void get_Website(string url){
    WSADATA wsaData;
    SOCKET Socket;
    SOCKADDR_IN SockAddr;
    int lineCount = 0;
    int rowCount = 0;
    struct hostent *host;
    string get_http;


    get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n";

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){
        cout << "WSAStartup failed.\n";
        system("pause");
        //return 1;
    }

    Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    host = gethostbyname(url.c_str());

    SockAddr.sin_port = htons(80);
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);

    if (connect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr)) != 0){
        cout << "Could not connect";
        system("pause");
        //return 1;
    }
    send(Socket, get_http.c_str(), strlen(get_http.c_str()), 0);

    int nDataLength;
    while ((nDataLength = recv(Socket, buffer, 10000, 0)) > 0){
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){

            website_HTML += buffer[i];
            i += 1;
        }
    }

    closesocket(Socket);
    WSACleanup();

}

Visual C++ 6.0 代码

    #include <string.h>
    #include <winsock2.h>
    #include <windows.h>
    #include <iostream>
    #include <vector>
    #include <locale>
    #include <sstream>
    using namespace std;
    #pragma comment(lib,"ws2_32.lib")


    string website_HTML;
    locale local;
    void get_Website(string url );
    char lineBuffer[200][80] ={' '};
    char buffer[10000];
    char ip_address[16];
    int i = 0, bufLen=0, j=0,lineCount=0;
    int lineIndex=0, posIndex=0;

     //****************************************************

    int main( void ){
        cout << "\n\n\n";
        get_Website("api.ipify.org" );
        for (size_t i=0; i<website_HTML.length(); ++i) website_HTML[i]= tolower(website_HTML[i],local);

        istringstream ss(website_HTML);
        string stoken;

        while(getline(ss, stoken, '\n')) {

                  strcpy(lineBuffer[lineIndex],stoken.c_str());
                  int dot=0;
                  for (int ii=0; ii< strlen( lineBuffer[lineIndex] ); ii++ ){

                      if (lineBuffer[lineIndex][ii] == '.') dot++;
                      if (dot>=3){
                          dot=0;
                          strcpy(ip_address,lineBuffer[lineIndex]);
                      }
                  }
                  lineIndex++;
         }
        cout<<"Your IP Address is  "<< ip_address<<" \n\n"; 

       cout<<"\nPress ANY key to close.\n\n";
       cin.ignore(); cin.get(); 


     return 0;
}

 //****************************************************

void get_Website(string url ){
    WSADATA wsaData;
    SOCKET Socket;
    SOCKADDR_IN SockAddr;
    int lineCount=0;
    int rowCount=0;
    struct hostent *host;
    string get_http;


    get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n";

    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
        cout << "WSAStartup failed.\n";
        system("pause");
        //return 1;
    }

    Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    host = gethostbyname(url.c_str());

    SockAddr.sin_port=htons(80);
    SockAddr.sin_family=AF_INET;
    SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);

    if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){
        cout << "Could not connect";
        system("pause");
        //return 1;
    }
    send(Socket,get_http.c_str(), strlen(get_http.c_str()),0 );

    int nDataLength;
    while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){        
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){

            website_HTML+=buffer[i];
            i += 1;
        }               
    }

    closesocket(Socket);
    WSACleanup();

}

【讨论】:

    【解决方案4】:

    理论上,您可以使用 Windows uPnP API 来执行此操作。您首先使用UPnPDeviceFinder 枚举Internet 网关设备。然后你得到一个网关的IUPnPRemoteEndpointInfo(嗯,通常只有一个,无论如何)并调用它的GetStringValue,传递一个包含"RemoteAddress"的字符串来获取它的远程地址(我think 表示它的外部地址,尽管我承认我并不完全确定)。哦,因为这是 COM,所以它必须是系统字符串,而不是普通字符串。

    从外部提供商处获取 IP 会更容易很多。在不使用任何 3rd 方库的情况下,它的代码如下所示:

    #include <windows.h>
    #include <wininet.h>
    #include <string>
    #include <iostream>
    
    std::string real_ip() { 
    
        HINTERNET net = InternetOpen("IP retriever",
            INTERNET_OPEN_TYPE_PRECONFIG,
            NULL,
            NULL,
            0);
    
        HINTERNET conn = InternetOpenUrl(net, 
                                         "http://myexternalip.com/raw", 
                                          NULL, 
                                          0, 
                                          INTERNET_FLAG_RELOAD, 
                                          0);
    
        char buffer[4096];
        DWORD read;
    
        InternetReadFile(conn, buffer, sizeof(buffer)/sizeof(buffer[0]), &read);
        InternetCloseHandle(net);    
    
        return std::string(buffer, read);
    }
    
    int main() {
        std::cout << real_ip() << "\n";
    }
    

    编译:

    cl ip_addr.cpp wininet.lib
    

    注意:如果您的机器配置为使用 IPv6,这可以(并且将会)检索您的 IPv6 地址。

    【讨论】:

    • 这个问题的最佳答案。
    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 2011-02-25
    • 2013-04-03
    • 2016-03-10
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多