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

2019-02-21 02:09发布

问题:

How to get IP address on C# in program which developing for android device in Unity 2018.2+?

Seems like Network.player.ipAddress is deprecated right now, so I'm looking for a new way.

回答1:

The Network.player.ipAddress has been deprecated since it is based on the old obsolete Unity networking system.

If you are using Unity's new uNet Network System, you can use NetworkManager.networkAddress;

string IP = NetworkManager.singleton.networkAddress;

If you are using raw networking protocol and APIs like TCP/UDP, you have to use the NetworkInterface API to find the IP Address. I use IPManager which has been working for me on both desktop and mobile devices:

IPv4:

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

IPv6:

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

The 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
}


回答2:

FINALLY resolved by Unity (from about version 2018.2)

Inside any [Command] arriving on the server,

the IP of the client, which, sent the [Command] is

        connectionToClient.address

Phew.


You will in fact have your own NetworkBehaviour derived class, you must do this. (It is very often called "Comms"):

public partial class Comms : NetworkBehaviour {

That class must override both the server/client startoffs, so that each Comms "knows what it is".

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 ..
}

On any actual project, since Unity doesn't do it, the clients must identify themselves to the server. You have to entirely handle that in Unity networking, from scratch.

(Note. that process is explained in the long blog here

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

So this would be the first thing you (must) do in OnStartLocalPlayer

public override void OnStartLocalPlayer() {

    Grid.commsLocalPlayer = this;

    StandardCodeYouMustHave_IdentifySelfToServer();

    // hundreds of other things to do..
    // hundreds of other things to do..
}

That "command pair" inside comms would look like this:

You must do this in all networking projects (or you will have no clue which client is which):

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
}

The good news is Unity have added the connectionToClient property, that is to say on the server, inside a [Command].

And then, you can indeed use the new .address property on that.

YOU CAN FINALLY GET THE IP OF THAT CLIENT!

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

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

It's that "simple", the ip of the client, which just connected, is "THEDAMNEDIP".

Phew.

Only took Unity 5? years.



回答3:

For 2018...

However, SEE THE NEWER ANSWER BELOW.

It does seem to be the case that you can now call - on the server, in NetworkManager - simply NetworkConnection#address and it will give it to you.

Recall that you must write your own NetworkManager, it does not work out of the box. Explained here if yer new to unity networking:

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

.

However note that

1) this only gives it to you on the server,

2) and only at "NetworkManager" which (of course!) is not actually when you identify each connecting client

Note the even newer answer - Unity finally resolved this.



回答4:

this is a minor modification to the excellent answer already provided by @Programmer. the motivation here is to improve functionality on MacOS and iOS. i haven't looked at android yet. i've also only tested IPV4.

what's different from @Programmer's original:

  1. for MacOS and iOS, include the filter on Interface Type
  2. .. but exclude the check on OperationalStatus
  3. move the enum into the class
  4. mark the class as static
  5. refactor so there's one method which returns all the matching IPs,
    and another to simply return the last one, which matches the logic of the original version.
  6. also included a diagnostic option for the list of IPs to include some details such as the "Description" of the interface (eg en0, en1). This option should obviously not be used if all you want is the IP address itself.
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;
    }
}