รับที่อยู่ IP ของเครื่อง


96

คำถามนี้เกือบจะเหมือนกับคำถามที่ถามก่อนหน้านี้Get the IP Address of local computer -Question แต่ผมต้องการที่จะหาที่อยู่ IP (e) ของเครื่องลินุกซ์

ดังนั้น: วิธีทำผม - โปรแกรมในภาษา C ++ - ตรวจสอบที่อยู่ IP ของเซิร์ฟเวอร์ลินุกซ์ใบสมัครของฉันทำงานอยู่ใน เซิร์ฟเวอร์จะมีที่อยู่ IP อย่างน้อยสองแห่งและฉันต้องการที่อยู่เฉพาะ (ที่อยู่ในเครือข่ายที่กำหนด (ที่อยู่สาธารณะ))

ฉันแน่ใจว่ามีฟังก์ชั่นง่ายๆที่จะทำ แต่ที่ไหนล่ะ


เพื่อให้สิ่งต่างๆชัดเจนขึ้น:

  • เซิร์ฟเวอร์จะมี "localhost": 127.0.0.1 อย่างชัดเจน
  • เซิร์ฟเวอร์จะมีที่อยู่ IP ภายใน (การจัดการ): 172.16.xx
  • เซิร์ฟเวอร์จะมีที่อยู่ IP ภายนอก (สาธารณะ): 80.190.xx

ฉันต้องการค้นหาที่อยู่ IP ภายนอกเพื่อผูกแอปพลิเคชันของฉันเข้ากับมัน เห็นได้ชัดว่าฉันสามารถเชื่อมโยงกับ INADDR_ANY ได้ (และนั่นคือสิ่งที่ฉันทำในขณะนี้) ฉันต้องการตรวจจับที่อยู่สาธารณะมากกว่า


3
ทำไมต้องทำเครื่องหมายป๊อปเพนของ ifconfig มันเป็นสิ่งที่ถูกต้องจริงๆ ไลบรารีเปลี่ยนไป แต่ ifconfig และ popen จะอยู่ที่นั่นเสมอ
Erik Aronesty

3
ไม่มีifconfig, routeฯลฯ จะเลิกสำหรับipคำสั่ง ตอนนี้คุณควรใช้สิ่งนั้นแทน
Anders

วันนี้ ifconfig ยังคงทำงานกับสถาปัตยกรรมมากกว่าแนวทางอื่น ๆ ดังนั้นให้เปลี่ยนเป็นคำสั่ง ip เมื่อใด / ถ้าเหมาะสม popen ยังคงเป็นทางออกที่ดีกว่า
Erik Aronesty

คำตอบ:


107

ฉันพบว่าโซลูชัน ioctl มีปัญหาบน os x (ซึ่งเป็นไปตาม POSIX ดังนั้นควรจะคล้ายกับ linux) อย่างไรก็ตาม getifaddress () จะช่วยให้คุณทำสิ่งเดียวกันได้อย่างง่ายดายมันทำงานได้ดีสำหรับฉันในระบบปฏิบัติการ x 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;
}

7
+1 และขอบคุณที่นำแนวทางดีๆนี้มาใช้แทน ioctl ที่น่าเบื่อเหล่านั้น! อย่างไรก็ตามการจัดการ IP6 ของคุณยังไม่ถูกต้องคุณควรส่งไปที่ sockaddr_in6 เช่นtmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey

2
@ แอนเดรย์ขอบคุณ ฉันได้อัปเดตคำตอบของฉันแล้ว (ยังไม่มีโอกาสทดสอบ)
Twelve47

2
ให้loและwlan0เชื่อมต่อในกรณีของฉัน สิ่งที่ฉันต้องการคือwlan0หรือeth0ที่มีการเชื่อมต่ออินเทอร์เน็ตดังนั้นฉันควรใช้strcmpหรือเป็นวิธีที่ดีกว่า
Sudip Bhattarai

28
  1. สร้างซ็อกเก็ต
  2. ดำเนินการ ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

อ่าน/usr/include/linux/if.hข้อมูลเกี่ยวกับifconfและifreqโครงสร้าง สิ่งนี้ควรให้ที่อยู่ IP ของแต่ละอินเทอร์เฟซบนระบบ อ่าน/usr/include/linux/sockios.hioctls เพิ่มเติมด้วย


ฉันรู้สึกประทับใจมันจะให้ที่อยู่ของอินเทอร์เฟซที่เชื่อมโยงกับคุณเท่านั้น
Matt Joiner

@MattJoiner ไม่ลองดูที่ man page SIOCGIFCONF จะแสดงข้อมูลบนอินเทอร์เฟซทั้งหมด ส่วนหนึ่งของหน้าคนที่ยกมาที่นี่: "ส่งคืนรายการที่อยู่ของอินเทอร์เฟซ (เลเยอร์การขนส่ง) ซึ่งปัจจุบันนี้หมายถึงเฉพาะที่อยู่ของตระกูล AF_INET (IPv4) สำหรับความเข้ากันได้ผู้ใช้ส่งผ่านโครงสร้าง ifconf เป็นอาร์กิวเมนต์ไปยัง ioctl ซึ่งมี ชี้ไปยังอาร์เรย์ของโครงสร้าง ifreq ใน ifc_req และความยาวเป็นไบต์ใน ifc_len เคอร์เนลจะเติม ifreqs ด้วยที่อยู่อินเตอร์เฟส L3 ปัจจุบันทั้งหมดที่กำลังทำงานอยู่ "
Stéphane

26

ผมชอบคำตอบของ jjvainio ดังที่ Zan Lnyx กล่าวว่าจะใช้ตารางการกำหนดเส้นทางภายในเพื่อค้นหาที่อยู่ IP ของอินเทอร์เฟซอีเธอร์เน็ตที่จะใช้สำหรับการเชื่อมต่อกับโฮสต์ภายนอกเฉพาะ ด้วยการใช้ซ็อกเก็ต UDP ที่เชื่อมต่อคุณจะได้รับข้อมูลโดยไม่ต้องส่งแพ็กเก็ตใด ๆ แนวทางนี้กำหนดให้คุณเลือกโฮสต์ภายนอกเฉพาะ โดยส่วนใหญ่แล้ว IP สาธารณะที่รู้จักกันดีควรทำกลอุบาย ฉันชอบที่อยู่เซิร์ฟเวอร์ DNS สาธารณะของ Google 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);
}

2
สิ่งนี้ทำให้ฉันเป็นส่วนตัว ip 127 *. *. * อาจเป็นเพราะฉันอยู่เบื้องหลัง NAT?
eugene

14

ดังที่คุณได้พบว่าไม่มี "ที่อยู่ IP ในเครื่อง" เพียงรายการเดียว ต่อไปนี้เป็นวิธีค้นหาที่อยู่ท้องถิ่นที่สามารถส่งไปยังโฮสต์เฉพาะได้

  1. สร้างซ็อกเก็ต UDP
  2. เชื่อมต่อซ็อกเก็ตกับที่อยู่ภายนอก (โฮสต์ที่จะได้รับที่อยู่ในเครื่องในที่สุด)
  3. ใช้ getsockname เพื่อรับที่อยู่ท้องถิ่น

ฉันไม่เคยทำแบบนี้ แต่ฉันชอบมัน ใช้ตารางเส้นทาง OS โดยอัตโนมัติและง่ายกว่าการสแกนรายการอินเทอร์เฟซมาก
Zan Lynx

5
สิ่งนี้จะถือว่าคุณรู้ว่าที่อยู่ภายนอกจะเป็นอย่างไร ซึ่งมักไม่เกิดขึ้นในศูนย์ข้อมูล
Christopher

คุณสามารถลองสามอย่างที่กำหนดให้กับทวีปต่างๆได้เสมอ (IANA ทำให้ง่ายขึ้น) และยอมรับสองในสามที่ดีที่สุด
Joshua

9

สิ่งนี้มีข้อได้เปรียบในการทำงานกับยูนิกซ์หลายรสชาติ ... และคุณสามารถปรับเปลี่ยนเล็กน้อยเพื่อทำงานกับ 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;
}

ขอบคุณสำหรับโซลูชันนี้ใช้งานได้ดีสำหรับฉันและมีประโยชน์มากในหลาย ๆ สถานการณ์เมื่อคุณต้องการรับข้อมูลจากโปรแกรม cl
zitroneneis

แม้ว่า ifconfig จะแสดงผลข้อมูลที่ต้องการ แต่ฉันก็ไม่แนะนำให้ใช้โปรแกรมใด ๆ ด้วยวิธีนี้ การเข้าสู่ซอร์สของ ifconfig และรับฟังก์ชันการทำงานนั้นดีกว่าการรันและแยกวิเคราะห์ผลลัพธ์
Zaur Nasibov

1
@MartinMeeser: ทำให้ภาษาเป็นกลางโดยมองหา "inet" แล้ว ":" ต่างหาก
Erik Aronesty

1
จากนั้นคุณมีภาษาอื่น ๆ ที่ไม่ได้ตั้งค่าอักขระตามภาษาละติน ifconfigลำพังคือการไม่ใช้เครื่องมือคำสั่งเช่นที่พวกเขาได้รับการเลิกชอบ คุณควรใช้ipตอนนี้ เหมาะสมวิธีการสร้างภาษาเอาท์พุทที่เป็นกลางคือการตั้งค่าLANGตัวแปรสภาพแวดล้อมไปCเช่น:LANG=C ls -l
เดส

1
ขอบคุณ. เป็นทางออกที่ง่ายและยอดเยี่ยม!
Ahmad Afkandeh

5

ฉันไม่คิดว่าจะมีคำตอบที่ถูกต้องสำหรับคำถามของคุณ มีวิธีมากมายในการเข้าใกล้สิ่งที่คุณต้องการ ดังนั้นฉันจะให้คำแนะนำบางอย่างในการทำให้เสร็จ

หากเครื่องมีอินเทอร์เฟซมากกว่า 2 อินเทอร์เฟซ ( loนับเป็นหนึ่ง) คุณจะมีปัญหาในการตรวจจับอินเทอร์เฟซที่ถูกต้องโดยอัตโนมัติ นี่คือสูตรอาหารบางส่วนเกี่ยวกับวิธีการทำ

ตัวอย่างเช่นปัญหาคือถ้าโฮสต์อยู่ใน DMZ หลังไฟร์วอลล์ NAT ซึ่งเปลี่ยน IP สาธารณะเป็น IP ส่วนตัวและส่งต่อคำขอ เครื่องของคุณอาจมีอินเทอร์เฟซ 10 รายการ แต่มีเพียงอินเทอร์เฟซเดียวเท่านั้นที่ตรงกับอินเทอร์เฟซสาธารณะ

แม้แต่การตรวจจับอัตโนมัติก็ไม่สามารถใช้งานได้ในกรณีที่คุณใช้ NAT แบบสองครั้งซึ่งไฟร์วอลล์ของคุณยังแปล source-IP เป็นสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง ดังนั้นคุณจึงไม่แน่ใจด้วยซ้ำว่าเส้นทางเริ่มต้นนำไปสู่อินเทอร์เฟซของคุณด้วยอินเทอร์เฟซสาธารณะ

ตรวจจับผ่านเส้นทางเริ่มต้น

นี่เป็นวิธีที่ฉันแนะนำในการตรวจจับสิ่งต่างๆโดยอัตโนมัติ

สิ่งที่คล้ายกันip r get 1.1.1.1มักจะบอกคุณถึงอินเทอร์เฟซที่มีเส้นทางเริ่มต้น

หากคุณต้องการสร้างสิ่งนี้ใหม่ในภาษาสคริปต์ / โปรแกรมที่คุณชื่นชอบให้ใช้strace ip r get 1.1.1.1และเดินตามถนนอิฐสีเหลือง

ตั้งค่าด้วย /etc/hosts

นี่คือคำแนะนำของฉันหากคุณต้องการควบคุม

คุณสามารถสร้างรายการใน/etc/hostslike

80.190.1.3 publicinterfaceip

จากนั้นคุณสามารถใช้นามแฝงนี้publicinterfaceipเพื่ออ้างถึงอินเทอร์เฟซสาธารณะของคุณ

น่าเศร้าที่haproxyไม่ต้องกังวลกับเคล็ดลับนี้กับ IPv6

ใช้สิ่งแวดล้อม

นี่เป็นวิธีแก้ปัญหาที่ดี/etc/hostsในกรณีที่คุณไม่อยู่root

เช่นเดียวกับ/etc/hosts. แต่ใช้สภาพแวดล้อมสำหรับสิ่งนี้ คุณสามารถลอง/etc/profileหรือ~/.profileสำหรับสิ่งนี้

ดังนั้นหากโปรแกรมของคุณต้องการตัวแปรMYPUBLICIPคุณสามารถรวมโค้ดเช่น (นี่คือ C อย่าลังเลที่จะสร้าง C ++ จากมัน):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }

ดังนั้นคุณสามารถเรียกสคริปต์ / โปรแกรมของคุณ/path/to/your/scriptเช่นนี้

MYPUBLICIP=80.190.1.3 /path/to/your/script

สิ่งนี้ยังใช้งานได้ในcrontab.

ระบุอินเทอร์เฟซทั้งหมดและกำจัดสิ่งที่คุณไม่ต้องการ

วิธีที่สิ้นหวังหากคุณไม่สามารถใช้ ip

หากคุณรู้ว่าคุณไม่ต้องการอะไรคุณสามารถแจกแจงอินเทอร์เฟซทั้งหมดและละเว้นอินเทอร์เฟซที่ผิดพลาดทั้งหมด

นี่ดูเหมือนจะเป็นคำตอบhttps://stackoverflow.com/a/265978/490291สำหรับแนวทางนี้

ทำเหมือน DLNA

วิธีการของคนเมาที่พยายามที่จะจมตัวเองในแอลกอฮอล์

คุณสามารถลองระบุเกตเวย์ UPnP ทั้งหมดบนเครือข่ายของคุณและด้วยวิธีนี้จะหาเส้นทางที่เหมาะสมสำหรับ "ภายนอก" บางอย่าง สิ่งนี้อาจอยู่บนเส้นทางที่เส้นทางเริ่มต้นของคุณไม่ได้ชี้ไป

ดูข้อมูลเพิ่มเติมได้ที่https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

สิ่งนี้ทำให้คุณรู้สึกประทับใจว่าอันไหนคืออินเทอร์เฟซสาธารณะที่แท้จริงของคุณแม้ว่าเส้นทางเริ่มต้นของคุณจะชี้ไปที่อื่นก็ตาม

มีมากยิ่งขึ้น

ที่ภูเขาตรงกับผู้เผยพระวจนะ

เราเตอร์ IPv6 โฆษณาตัวเองเพื่อให้คำนำหน้า IPv6 ที่ถูกต้องแก่คุณ การดูคำนำหน้าจะช่วยให้คุณทราบว่ามี IP ภายในหรือ Global หรือไม่

คุณสามารถฟัง IGMP หรือเฟรม IBGP เพื่อค้นหาเกตเวย์ที่เหมาะสม

มีที่อยู่ IP น้อยกว่า 2 ^ 32 ดังนั้นจึงใช้เวลาไม่นานบน LAN ในการส่ง Ping ทั้งหมด สิ่งนี้ให้คำแนะนำทางสถิติว่าส่วนใหญ่ของอินเทอร์เน็ตอยู่ที่ใดจากมุมมองของคุณ อย่างไรก็ตามคุณควรมีเหตุผลมากกว่าhttps://de.wikipedia.org/wiki/SQL_Slammer ที่มีชื่อเสียง

ICMP และแม้แต่ ARP เป็นแหล่งข้อมูลที่ดีสำหรับข้อมูลเครือข่ายไซด์แบนด์ มันอาจช่วยคุณได้เช่นกัน

คุณสามารถใช้ที่อยู่ Ethernet Broadcast เพื่อติดต่อกับอุปกรณ์โครงสร้างพื้นฐานเครือข่ายทั้งหมดของคุณซึ่งมักจะช่วยได้เช่น DHCP (แม้กระทั่ง DHCPv6) เป็นต้น

รายการเพิ่มเติมนี้อาจไม่มีที่สิ้นสุดและไม่สมบูรณ์เสมอไปเนื่องจากผู้ผลิตอุปกรณ์เครือข่ายทุกรายกำลังคิดค้นช่องโหว่ด้านความปลอดภัยใหม่ ๆ เกี่ยวกับวิธีตรวจจับอุปกรณ์ของตนเองโดยอัตโนมัติ ซึ่งมักจะช่วยได้มากในการตรวจจับอินเทอร์เฟซสาธารณะบางส่วนที่ไม่มีส่วนต่อประสาน

'Nuff กล่าวว่า. ออก.


4

นอกจากนี้สิ่งที่สตีฟเบเกอร์ได้กล่าวว่าคุณสามารถหาคำอธิบายของ IOCTL SIOCGIFCONF ในที่netdevice (7) หน้าคน

เมื่อคุณมีรายการที่อยู่ IP ทั้งหมดบนโฮสต์แล้วคุณจะต้องใช้ตรรกะเฉพาะของแอปพลิเคชันเพื่อกรองที่อยู่ที่คุณไม่ต้องการออกและหวังว่าคุณจะมีที่อยู่ IP เหลืออยู่


4

อย่าเขียนโค้ดให้ยาก: นี่คือสิ่งที่สามารถเปลี่ยนแปลงได้ หลายโปรแกรมคิดว่าจะผูกเข้ากับอะไรโดยการอ่านในไฟล์ config และทำอะไรก็ได้ ด้วยวิธีนี้หากในอนาคตโปรแกรมของคุณจำเป็นต้องเชื่อมโยงกับสิ่งที่ไม่ใช่ IP สาธารณะก็สามารถทำได้


2

ตามที่คำถามระบุ Linux เทคนิคที่ฉันชอบที่สุดในการค้นหาที่อยู่ IP ของเครื่องคือการใช้ netlink ด้วยการสร้างซ็อกเก็ต netlink ของโปรโตคอล NETLINK_ROUTE และส่ง RTM_GETADDR แอปพลิเคชันของคุณจะได้รับข้อความที่มีที่อยู่ IP ที่มีอยู่ทั้งหมด ตัวอย่างมีให้ที่นี่

ในการจัดการข้อความเพียงบางส่วน libmnl จึงสะดวก หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกต่างๆของ NETLINK_ROUTE (และวิธีการแยกวิเคราะห์) แหล่งที่มาที่ดีที่สุดคือซอร์สโค้ดของ iproute2 (โดยเฉพาะแอปพลิเคชันมอนิเตอร์) รวมถึงฟังก์ชันรับในเคอร์เนล หน้า man ของrtnetlinkยังมีข้อมูลที่เป็นประโยชน์


1

คุณสามารถผสานรวมกับ curl ได้อย่างง่ายดายเช่น: curl www.whatismyip.orgจากเชลล์คุณจะได้รับไอพีระดับโลกของคุณ คุณเป็นคนที่พึ่งพาเซิร์ฟเวอร์ภายนอกบางตัว แต่คุณจะอยู่เบื้องหลัง NAT เสมอ


ฉันไม่เข้าใจว่าทำไมผู้คนจำนวนมากไม่ใช้ไซต์นี้ พวกเขายังมีหน้าที่อ่านได้ด้วยเครื่องดังนั้นคุณจึงไม่ต้องคัดกรองข้อมูลที่สนใจ
deft_code

1
คุณอาจไม่ได้รับ IP ที่ถูกต้องหากคุณใช้งานภายในพร็อกซี
แจ็ค

1
icanhazip.comส่งคืน IP และไม่มีอะไรเพิ่มเติม เหมาะสำหรับการฝังในสคริปต์ทุบตี!
lukecyca

ipinfo.io/ipเป็นอีกทางเลือกหนึ่ง หากคุณ curl ipinfo.ioจะส่งคืนชื่อโฮสต์ข้อมูลตำแหน่งทางภูมิศาสตร์และอื่น ๆ ในรูปแบบ JSON
Ben Dowling


-10

// 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();

}


10
นี่เป็นปัญหาเฉพาะสำหรับ Windows เท่านั้น (คำถามเกี่ยวกับ linux) ซึ่งมีรูปแบบไม่ถูกต้องซึ่งอาศัยบริการที่ไม่อยู่ในการควบคุมภายในผ่านการแยกวิเคราะห์เนื้อหาเว็บไซต์ที่เปราะบาง ไม่พบด้านบวกใด ๆ ของ "วิธีแก้ปัญหา" นี้
viraptor

1
ห๊ะ! การแยกวิเคราะห์ HTML ยังมองหาความคิดเห็นที่เป็นคำเตือนเกี่ยวกับสิ่งที่กำลังทำอยู่
notlesh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.