98

この質問は、以前に尋ねられたローカル コンピューターの IP アドレスを取得するにはどうすればよいですか?とほとんど同じです。-質問。ただし、 Linux Machineの IP アドレスを見つける必要があります。

では、どうすれば - C++でプログラム的に -アプリケーションが実行されている Linux サーバーの IP アドレスを検出できますか。サーバーには少なくとも 2 つの IP アドレスがあり、特定の IP アドレス (特定のネットワーク (パブリック ネットワーク) 内の IP アドレス) が必要です。

それを行うための簡単な機能があると確信していますが、どこですか?


物事をもう少し明確にするために:

  • サーバーには明らかに「localhost」があります:127.0.0.1
  • サーバーには内部 (管理) IP アドレスがあります: 172.16.xx
  • サーバーには外部 (パブリック) IP アドレスがあります: 80.190.xx

アプリケーションをバインドするための外部 IP アドレスを見つける必要があります。明らかに、私は INADDR_ANY にバインドすることもできます (そして、実際にそれが現在私が行っていることです)。ただし、パブリックアドレスを検出したいと思います。

4

12 に答える 12

112

ioctlソリューションはosxで問題があることがわかりました(これはPOSIXに準拠しているため、Linuxと同様である必要があります)。ただし、getifaddress()を使用すると、同じことを簡単に実行できます。osx 10.5では問題なく動作し、以下でも同じである必要があります。

以下の簡単な例を実行しました。これは、マシンのすべてのIPv4アドレスを出力します(getifaddrsが成功したこと、つまり0を返すことも確認する必要があります)。

IPv6アドレスも表示するように更新しました。

#include <stdio.h>      
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>

int main (int argc, const char * argv[]) {
    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) {
            continue;
        }
        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);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } 
    }
    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
    return 0;
}
于 2008-11-05T17:34:43.260 に答える
28
  1. ソケットを作成します。
  2. 実行ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

および構造体/usr/include/linux/if.hについての情報を参照してください。これにより、システム上の各インターフェースの IP アドレスが得られます。追加の ioctl についてもお読みください。ifconfifreq/usr/include/linux/sockios.h

于 2008-10-17T15:38:32.267 に答える
26

jjvainioの答えが好きです。Zan Lnyx が言うように、ローカル ルーティング テーブルを使用して、特定の外部ホストへの接続に使用されるイーサネット インターフェイスの IP アドレスを見つけます。接続された UDP ソケットを使用すると、実際にパケットを送信しなくても情報を取得できます。このアプローチでは、特定の外部ホストを選択する必要があります。ほとんどの場合、よく知られているパブリック IP でうまくいくはずです。この目的には、Google のパブリック DNS サーバー アドレス 8.8.8.8 が気に入っていますが、別の外部ホスト IP を選択したい場合もあります。完全なアプローチを示すコードを次に示します。

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}
于 2010-06-25T18:15:55.927 に答える
14

お気づきのように、単一の「ローカル IP アドレス」などというものはありません。特定のホストに送信できるローカルアドレスを見つける方法は次のとおりです。

  1. UDP ソケットを作成する
  2. ソケットを外部アドレス (最終的にローカル アドレスを受け取るホスト) に接続します。
  3. getsockname を使用してローカル アドレスを取得する
于 2008-10-18T17:51:42.150 に答える
9

これには、UNIX の多くのフレーバーで動作するという利点があります ...そして、任意の o/s で動作するように簡単に変更できます。上記のすべてのソリューションでは、月の満ち欠けに応じてコンパイラ エラーが発生します。それを行う良いPOSIXの方法がある瞬間...これを使用しないでください(これが書かれた時点ではそうではありませんでした)。

// ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/'

#include <stdlib.h>

int main() {
        setenv("LANG","C",1);
        FILE * fp = popen("ifconfig", "r");
        if (fp) {
                char *p=NULL, *e; size_t n;
                while ((getline(&p, &n, fp) > 0) && p) {
                   if (p = strstr(p, "inet ")) {
                        p+=5;
                        if (p = strchr(p, ':')) {
                            ++p;
                            if (e = strchr(p, ' ')) {
                                 *e='\0';
                                 printf("%s\n", p);
                            }
                        }
                   }
              }
        }
        pclose(fp);
        return 0;
}
于 2011-05-18T13:23:50.557 に答える
5

Steve Baker の発言に加えて、netdevice(7) man ページに SIOCGIFCONF ioctl の説明があります。

ホスト上のすべての IP アドレスのリストを取得したら、アプリケーション固有のロジックを使用して不要なアドレスを除外し、IP アドレスが 1 つ残っていることを確認する必要があります。

于 2008-10-18T09:24:50.727 に答える
4

ハードコーディングしないでください。これは変更可能な種類のものです。多くのプログラムは、構成ファイルを読み込んで何をバインドするかを判断し、その内容を実行します。このようにして、将来、プログラムがパブリック IP ではないものにバインドする必要が生じた場合に、それを行うことができます。

于 2010-07-12T04:41:54.603 に答える
2

質問は Linux を指定しているため、マシンの IP アドレスを検出するための私のお気に入りの手法は、netlink を使用することです。プロトコル NETLINK_ROUTE の netlink ソケットを作成し、RTM_GETADDR を送信することにより、アプリケーションは利用可能なすべての IP アドレスを含むメッセージを受信します。ここに例を示します

メッセージ処理の一部を単純化するには、libmnl が便利です。NETLINK_ROUTE のさまざまなオプション (およびそれらがどのように解析されるか) について詳しく知りたい場合は、最適なソースはiproute2 (特にモニター アプリケーション)のソース コードとカーネルの受信関数です。rtnetlinkの man ページにも役立つ情報が含まれています。

于 2013-06-27T22:00:28.690 に答える
0

次のように簡単に curl との統合を行うことができcurl www.whatismyip.orgます。シェルからグローバル IP を取得します。あなたはある種の外部サーバーに依存していますが、NAT の背後にいる場合は常にそうなります。

于 2008-10-17T18:13:55.447 に答える
-12
// Use a HTTP request to a well known server that echo's back the public IP address
void GetPublicIP(CString & csIP)
{
    // Initialize COM
    bool bInit = false;
    if (SUCCEEDED(CoInitialize(NULL)))
    {
        // COM was initialized
        bInit = true;

        // Create a HTTP request object
        MSXML2::IXMLHTTPRequestPtr HTTPRequest;
        HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP");
        if (SUCCEEDED(hr))
        {
            // Build a request to a web site that returns the public IP address
            VARIANT Async;
            Async.vt = VT_BOOL;
            Async.boolVal = VARIANT_FALSE;
            CComBSTR ccbRequest = L"http://whatismyipaddress.com/";

            // Open the request
            if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async)))
            {
                // Send the request
                if (SUCCEEDED(HTTPRequest->raw_send()))
                {
                    // Get the response
                    CString csRequest = HTTPRequest->GetresponseText();

                    // Parse the IP address
                    CString csMarker = "<!-- contact us before using a script to get your IP address -->";
                    int iPos = csRequest.Find(csMarker);
                    if (iPos == -1)
                        return;
                    iPos += csMarker.GetLength();
                    int iPos2 = csRequest.Find(csMarker,iPos);
                    if (iPos2 == -1)
                        return;

                    // Build the IP address
                    int nCount = iPos2 - iPos;
                    csIP = csRequest.Mid(iPos,nCount);
                }
            }
        }
    }

    // Unitialize COM
    if (bInit)
        CoUninitialize();
}

于 2011-03-27T22:03:06.710 に答える