如何在 Unity 2018+ 中获取设备的 IP 地址?

How to get IP address of device in Unity 2018+?

如何在 Unity 2018.2+ 中为 android 设备开发的程序中在 C# 上获取 IP 地址?

似乎 Network.player.ipAddress 现在已被弃用,所以我正在寻找新方法。

Network.player.ipAddress 已被弃用,因为它基于旧的过时的 Unity 网络系统。

如果你使用的是Unity新的uNet网络系统,你可以使用NetworkManager.networkAddress;

string IP = NetworkManager.singleton.networkAddress;

如果您使用的是原始网络协议并且 API 类似于 TCP/UDP,您必须使用 NetworkInterface API 来查找 IP 地址。我使用 IPManager,它在桌面和移动设备上都对我有用:

IPv4:

string ipv4 = IPManager.GetIP(ADDRESSFAM.IPv4);

IPv6:

string ipv6 = IPManager.GetIP(ADDRESSFAM.IPv6);

IPManager class:

using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

public class IPManager
{
    public static string GetIP(ADDRESSFAM Addfam)
    {
        //Return null if ADDRESSFAM is Ipv6 but Os does not support it
        if (Addfam == ADDRESSFAM.IPv6 && !Socket.OSSupportsIPv6)
        {
            return null;
        }

        string output = "";

        foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
        {
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
            NetworkInterfaceType _type1 = NetworkInterfaceType.Wireless80211;
            NetworkInterfaceType _type2 = NetworkInterfaceType.Ethernet;

            if ((item.NetworkInterfaceType == _type1 || item.NetworkInterfaceType == _type2) && item.OperationalStatus == OperationalStatus.Up)
#endif 
            {
                foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
                {
                    //IPv4
                    if (Addfam == ADDRESSFAM.IPv4)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            output = ip.Address.ToString();
                        }
                    }

                    //IPv6
                    else if (Addfam == ADDRESSFAM.IPv6)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
                        {
                            output = ip.Address.ToString();
                        }
                    }
                }
            }
        }
        return output;
    }
}

public enum ADDRESSFAM
{
    IPv4, IPv6
}

2018 年...

但是,请参阅下面的更新答案。

您现在似乎确实可以调用 - 在服务器上,在 NetworkManager 中 - 只需 NetworkConnection#address 它就会给你。

回想一下,您必须 编写自己的 NetworkManager,它不是开箱即用的。如果您是统一网络的新手,请在此处进行解释:

https://forum.unity.com/threads/networkmanager-error-server-client-disconnect-error-1.439245/#post-3754939

public class OurNetworkManager : NetworkManager {

    //
    // server .............
    //

    public override void OnServerConnect(NetworkConnection nc) {

        // it means "here on the server" one of the clients has connected
        base.OnServerConnect(nc);

        Log("a client connected " + nc.connectionId + " " + nc.hostId);
        Log("address? " + nc.address);
    }

    public override void OnServerDisconnect(NetworkConnection nc) { .. etc

但是请注意

1) 这只会在服务器,

上给你

2) 并且仅在 "NetworkManager" 处,这(当然!)是 实际上当您识别每个连接的客户端时

注意更新的答案 - Unity 终于解决了这个问题。

最终由 Unity 解决(大约从 2018.2 版开始)

在到达服务器的任何 [Command] 中,

发送[Command]的客户端IP是

        connectionToClient.address

呼。


您实际上将拥有自己的 NetworkBehaviour 派生 class,您必须这样做。 (通常称为“Comms”):

public partial class Comms : NetworkBehaviour {

class 必须覆盖两个 server/client 启动,以便每个 Comms“知道它是什么”。

public override void OnStartServer() {
    .. this "Comms" does know it IS the server ..
}

public override void OnStartLocalPlayer() {
    .. this "Comms" does know it IS one of the clients ..
}

在任何实际项目中,由于 Unity 不这样做,客户端必须向服务器表明自己的身份。您必须从头开始在 Unity 网络中完全处理它。

(注意。该过程在此处的长博客中进行了解释

https://forum.unity.com/threads/networkmanager-error-server-client-disconnect-error-1.439245/#post-3754939)

所以这将是您(必须)在 OnStartLocalPlayer

中做的第一件事
public override void OnStartLocalPlayer() {
    
    Grid.commsLocalPlayer = this;

    StandardCodeYouMustHave_IdentifySelfToServer();
    
    // hundreds of other things to do..
    // hundreds of other things to do..
}

comms 中的“命令对”如下所示:

你必须在所有网络项目中这样做(否则你将不知道哪个客户端是哪个):

public void StandardCodeYouMustHave_IdentifySelfToServer() {
    
    string identityString = "quarterback" "linebacker" "johnny smith"
        .. or whatever this device is ..

    CmdIdentifySelfToServer( identityString );

    // hundreds more lines of your code
    // hundreds more lines of your code
}

[Command]
void CmdIdentifySelfToServer(string connectingClientIdString) {
    
    .. you now know this connection is a "connectingClientIdString"
    .. and Unity gives you the connection: in the property: connectionToClient
    .. you must record this in a hash, database, or whatever you like

    MakeANote( connectingClientIdString , connectionToClient )

    // hundreds more lines of your code
    // hundreds more lines of your code
}

好在Unity已经添加了connectionToClient属性,也就是说在服务器里面,有一个[Command].

然后,您确实可以在上面使用新的 .address 属性。

您终于可以获取该客户端的 IP 了!

public void StandardCodeYouMustHave_IdentifySelfToServer() {
    CmdIdentifySelfToServer( identityString );
}

[Command]
void CmdIdentifySelfToServer(string connectingClientIdString) {
    MakeANote( connectingClientIdString , connectionToClient )
    string THEDAMNEDIP = connectionToClient.address;
}

就这么“简单”,刚刚连接的客户端ip是“THEDAMNEDIP”。

呼。

只学了Unity 5?年。

这是对 的细微修改。这里的动机是改进 MacOS 和 iOS 上的功能。我还没有看过 android。我也只测试过 IPV4。

与@Programmer 的原文有何不同:

  1. 对于 MacOS 和 iOS,包括接口类型过滤器
  2. ..但排除对 OperationalStatus
  3. 的检查
  4. 将枚举移动到 class
  5. 将 class 标记为静态
  6. 重构所以有一种方法return所有匹配的 IP,
    另一个简单地return最后一个,这符合原始版本的逻辑。
  7. 还包括 IP 列表的诊断选项,以包含一些详细信息,例如接口的 "Description"(例如 en0en1)。如果您只需要 IP 地址本身,则显然不应使用此选项。
public static class IPManager
{
    public enum ADDRESSFAM
    {
        IPv4, IPv6
    }

    public static string GetIP(ADDRESSFAM Addfam)
    {
      string ret = "";
      List<string> IPs = GetAllIPs(Addfam, false);
      if (IPs.Count > 0) {
        ret = IPs[IPs.Count - 1];
      }
      return ret;
    }

    public static List<string> GetAllIPs(ADDRESSFAM Addfam, bool includeDetails)
    {
        //Return null if ADDRESSFAM is Ipv6 but Os does not support it
        if (Addfam == ADDRESSFAM.IPv6 && !Socket.OSSupportsIPv6)
        {
            return null;
        }

        List<string> output = new List<string>();

        foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
        {

#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS
            NetworkInterfaceType _type1 = NetworkInterfaceType.Wireless80211;
            NetworkInterfaceType _type2 = NetworkInterfaceType.Ethernet;

            bool isCandidate = (item.NetworkInterfaceType == _type1 || item.NetworkInterfaceType == _type2);

#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
            // as of MacOS (10.13) and iOS (12.1), OperationalStatus seems to be always "Unknown".
            isCandidate = isCandidate && item.OperationalStatus == OperationalStatus.Up;
#endif

            if (isCandidate)
#endif 
            {
                foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
                {
                    //IPv4
                    if (Addfam == ADDRESSFAM.IPv4)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            string s = ip.Address.ToString();
                            if (includeDetails) {
                                s += "  " + item.Description.PadLeft(6) + item.NetworkInterfaceType.ToString().PadLeft(10);
                            }
                            output.Add(s);
                        }
                    }

                    //IPv6
                    else if (Addfam == ADDRESSFAM.IPv6)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
                        {
                            output.Add(ip.Address.ToString());
                        }
                    }
                }
            }
        }
        return output;
    }
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

public class TestLocationService : MonoBehaviour
{
    public Text hintText;

    private void Start()
    {
        GetLocalIPAddress();
    }
    public string GetLocalIPAddress()
    {
        var host = Dns.GetHostEntry(Dns.GetHostName());
        foreach (var ip in host.AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                hintText.text = ip.ToString();
                return ip.ToString();
            }
        }
        throw new System.Exception("No network adapters with an IPv4 address in the system!");
    }

}
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System;
using UnityEngine.Networking;
using Newtonsoft.Json;

public class geoplugin
{
    //Assume E.g. before each comment
    public string geoplugin_request; //THIS IS THE IP ADDRESS
    public int geoplugin_status; //200
    public string geoplugin_delay; //"1ms"
    public string geoplugin_credit;
    public string geoplugin_city; //E.g Toronto. 
    public string geoplugin_region; //E.g. Ontario
    public string geoplugin_regionCode; //E.g. ON
    public string geoplugin_regionName; //Also Ontario
    public string geoplugin_areaCode; //idk
    public string geoplugin_dmaCode; //idk
    public string geoplugin_countryCode; //E.g. CA (that's Canada)
    public string geoplugin_countryName; //E.g. Canada
    public int geoplugin_inEU; //0 if not in EU
    public bool geoplugin_euVATrate; //false
    public string geoplugin_continentCode; //E.g. NA
    public string geoplugin_continentName; //North America
    public string geoplugin_latitude; 
    public string geoplugin_longitude; 
    public string geoplugin_locationAccuracyRadius; //"1"
    public string geoplugin_timezone; //"America\/Toronto",
    public string geoplugin_currencyCode; //"CAD",
    public string geoplugin_currencySymbol; //"$",
    public string geoplugin_currencySymbol_UTF8; //$",
    public float geoplugin_currencyConverter;
}

public class GetMyIP : MonoBehaviour 
{
    geoplugin geoInfo;
    string ip;


    public void GetIP()
    {
        StartCoroutine(GetRequest("http://www.geoplugin.net/json.gp", (UnityWebRequest req) =>
        {
            if (req.isNetworkError || req.isHttpError)
            {
                Debug.Log($"{req.error}: {req.downloadHandler.text}");
            }
            else
            {
                geoplugin geopluginData = JsonConvert.DeserializeObject<geoplugin>(req.downloadHandler.text);

                ip = geopluginData.geoplugin_request;
                geoInfo = geopluginData;
                Debug.Log(ip);

                //AT THIS POINT IN THE CODE YOU CAN DO WHATEVER YOU WANT WITH THE IP ADDRESS
            }
        }));
    }

    IEnumerator GetRequest(string endpoint, Action<UnityWebRequest> callback)
    {
        using (UnityWebRequest request = UnityWebRequest.Get(endpoint))
        {
            // Send the request and wait for a response
            yield return request.SendWebRequest();

            callback(request);
        }
    }
}

这应该会为您提供存储在字符串 ip 中的 IP 地址,您可以随意使用它。也有可能这会为您提供很多信息,而这些信息是您首先需要的 IP 地址。

反正那是我的经历。我试图创建一个与真实世界天气相匹配的游戏内天气系统,我希望 ip 地址可以找到位置,但这个网站同时提供了这两者,无需先获取 ip 地址。

这是我用来理解这段代码的教程:https://theslidefactory.com/using-unitywebrequest-to-download-resources-in-unity/

您还需要导入此代码才能正常工作:https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347

如果您只想要 ip 地址而不需要其他任何东西,那么这不是最佳解决方案。然而,大多数时候,您想对 ip 地址做一些事情,比如获取位置,这提供了很多您可能想要获取的东西。

关于地理过滤器数据 post,根据他们的条款: “限制。 一般的。您不得,也不得允许他人:

ix:将服务用于识别或定位特定个人或家庭的目的。

所以我不会为此依赖他们,因为它 'illegal?' 或者至少违反了他们的政策。