3

如何使用 windows 库获取 NAT 的外部 IP 地址?我正在尝试查找有关 INATExternalIPAddressCallback 的任何信息,但仅在 C++ 中找到了一个使用不可用 C# 接口的示例。任何指导将不胜感激。

-卡尔

4

3 回答 3

2

抱歉,我没有使用 Windows 中使用 UPNP 服务的现有 API 来回答,但它可能会对您有所帮助

你也可以在互联网上使用 STUN 服务器,有很多开放的,每个 VOIP 提供商都有一个。 http://en.wikipedia.org/wiki/STUN https://www.rfc-editor.org/rfc/rfc3489

例如,打开一个 UDP 套接字,在 3478 端口上连接到 stun.gmx.net 并发送一个这样的包:

using System;
using System.Net;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace xxx
{
class STUNPacket
{
    static RNGCryptoServiceProvider m_rand=new RNGCryptoServiceProvider();

    private byte[] m_requestid=new byte[16];

    public STUNPacket()
    {
        m_rand.GetBytes(m_requestid);
    }

    public byte[] CreateRequest()
    {
        byte[] packet=new byte[0x1c] {
            //Header:
            0x00,0x01,      //Art des STUN-Pakets: 0x0001=Binding Request
            0x00,0x08,      //Länge des Pakets ohne Header
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    //Transkations-ID
            //Message-Attribute:
            0x00,0x03,      //Typ: Change-Request
            0x00,0x04,      //Länge: 4 Bytes
            0x00,0x00,0x00,0x00 //Weder IP, noch Port ändern
        };

        for(int i=0;i<16;i++)
            packet[i+4]=m_requestid[i];

        return packet;
    }

    public IPEndPoint ParseResponse(byte[] paket)
    {
        if (paket.Length<20)
            throw new Exception("STUN-Response zu kurz, um einen Header zu haben");
        if (paket[0]!=1||paket[1]!=1)
            throw new Exception("STUN-Reposne hat falschen Typ, muss Binding-Response sein");
        for (int i=0;i<16;i++)
            if (paket[i+4]!=m_requestid[i])
                throw new Exception("STUN-Antwort hat falsche Transkations-ID");
        int size=paket[2]*256+paket[3];
        int pos=20;
        for(;;)
        {
            if (size<4)
                throw new Exception("Verbleibende Nachricht zu kurz für weiteren Attributheader, "+
                    "Mapped-Adress-Attribut nicht gefunden");
            int attrsize=paket[pos+2]*256+paket[pos+3];
            if (pos+4+attrsize>paket.Length)
                throw new Exception("Attributheader hat Länge, die nicht mehr ins Paket passt");
            //Wenn kein Mapped-Adress-Attribut, dann überspringen
            if (paket[pos]!=0||paket[pos+1]!=1)
            {
                pos+=attrsize;
                continue;
            }
            if (attrsize<8)
                throw new Exception("Mapped-Adress-Attribut ist zu kurz");
            if (paket[pos+5]!=1)
                throw new Exception("Adreßfamilie von Mapped-Adress ist nicht IPV4");
            int port=paket[pos+6]*256+paket[pos+7];
            IPAddress adress=new IPAddress(new byte[4] { paket[pos+8],paket[pos+9],paket[pos+10],paket[pos+11] });
            return new IPEndPoint(adress,port);
        }
    }

}

}

ParseResponse 返回的 IP 地址应该是你的 ip 地址,就像外界看到的一样。

请注意,如果没有 Internet 服务器的帮助或直接通过服务器上的 upnp,将无法获取您的外部 IP

于 2009-05-04T13:14:12.580 回答
1

在 CodeProject 上,harold aptroot 的以下文章描述了您想要做什么:

在 C# 中使用 UPnP 进行 NAT 遍历,无需任何库。 http://www.codeproject.com/KB/IP/upnpnattraversal.aspx

当然,这仅在您的路由器支持 UPnP 时才有效。

来自 MSDN—— http: //msdn.microsoft.com/en-us/library/aa365074 (VS.85).aspx :

INATExternalIPAddressCallback 接口由具有 UPnP 技术的 NAT 应用程序实现。它提供了一个方法,如果 NAT 计算机的外部 IP 地址发生变化,系统调用该方法。

您是尝试获取一次外部 IP 地址,还是希望在它发生更改时收到通知。如果您只想获取当前的外部 IP 地址,则不需要为 INATExternalIPAddressCallback 实现回调

于 2009-04-28T22:47:15.467 回答
1

NAT 背后的系统不知道它是“真实”IP。事实上,就操作系统而言,它与任何其他操作系统一样拥有 IP。它只是路由到定义的网关,而不必担心细节。配置的 IP 不是广播地址(10.whatever、192.168.whatever)这一事实对操作系统来说并不重要。

获取外部 IP 的唯一方法是查询一些外部系统。它可能是您的 UPnP 路由器(理论上,它可能会返回另一个非广播地址),也可能是互联网上的某个外部服务器(例如,这个“STUN”的东西)。

如果是我,我只会在我的网站上托管我自己的服务。您可以在大多数支持 PHP 的网站上抛出一个简单的 php 脚本:

<?php echo $REMOTE_ADDR; >

这将回退客户端的 IP 地址。接下来,您必须编写客户端代码来获取该脚本的结果(在 C# 中相当容易)。

于 2009-05-18T19:15:24.907 回答