Search Unity

Question Do I have to make port trigger etc for external IP connection with com.unity.transport?

Discussion in 'Unity Transport' started by Ozgekocaoglu, May 25, 2022.

  1. Ozgekocaoglu

    Ozgekocaoglu

    Joined:
    Feb 13, 2019
    Posts:
    38
    Hello everyone,

    I'm creating a multiplayer game using (com.unity.transport) and Lobby services at Unity. I'm gonna describe my steps:

    1.I've created a lobby.
    2. I've kept host external IP using lobby options -> data object.
    3. I've created a driver as host as you can see:

    Code (CSharp):
    1.   IEnumerator startNetworkAsHost()
    2.   {
    3.     HostDriver = NetworkDriver.Create();
    4.     if (HostDriver.Bind(NetworkEndPoint.AnyIpv4) != 0)
    5.     {
    6.       Debug.LogError("Server failed to bind");
    7.     }
    8.     else
    9.     {
    10.       while (!HostDriver.Bound)
    11.       {
    12.         HostDriver.ScheduleUpdate().Complete();
    13.         yield return null;
    14.       }
    15.  
    16.       if (HostDriver.Listen() != 0)
    17.       {
    18.         Debug.LogError("Server failed to listen");
    19.       }
    20.       else
    21.       {
    22.         Debug.Log("Server connected");
    23.       }
    24.     }
    25.   }

    4. I've reached host external ip when i join the lobby.
    5. I've created a client driver and connected with host's external IP.

    Code (CSharp):
    1.   // -------------------------------------------------------------------------
    2.   public void startNetworkAsClient()
    3.   {
    4.     PlayerDriver = NetworkDriver.Create();
    5.     System.Net.IPAddress serverAddress = System.Net.IPAddress.Parse(endPointRemote);
    6.  
    7.     Unity.Collections.NativeArray<byte> nativeArrayAddress;
    8.     nativeArrayAddress = new  Unity.Collections.NativeArray<byte>(serverAddress.GetAddressBytes().Length, Unity.Collections.Allocator.Temp);  
    9.     nativeArrayAddress.CopyFrom(serverAddress.GetAddressBytes());
    10.    
    11.     NetworkEndPoint endpoint = NetworkEndPoint.LoopbackIpv4;
    12.     endpoint.SetRawAddressBytes(nativeArrayAddress);
    13.     endpoint.Port = portNumber;
    14.     clientConnection = PlayerDriver.Connect(endpoint);
    15.     Debug.Log("Connected: " + clientConnection.IsCreated);
    16.   }
    17.  
    6. I'm trying to accept connections from server using that code block:

    Code (CSharp):
    1.  if(currentLobby.HostId == loggedPlayer.Id){
    2.         HostDriver.ScheduleUpdate().Complete();
    3.  
    4.         NetworkConnection c;
    5.         while ((c = HostDriver.Accept()) != default(NetworkConnection))
    6.         {
    7.             hostConnections.Add(c);
    8.             Debug.Log("Accepted a connection");
    9.         }
    10. }
    But it doesn't work. So, should i connect to the host not directly with external IP? Should i use port triggering or something?
     
  2. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    Are your host and client on the same network? If not, you'll need to set up port forwarding on your host's router, or set up some kind of NAT traversal, or (perhaps simpler) use a relay service.

    Also, are you scheduling regular updates of both the host and client drivers? The
    Connect
    method doesn't actually do much on its own. The connection request will only be sent in the next
    ScheduleUpdate
    (or
    ScheduleFlushSend
    ).

    How are you finding out which port to connect to on the host? Binding to
    NetworkEndPoint.AnyIpv4
    will have the OS select a port randomly from a predefined range (called an ephemeral port). This is fine for clients, but for hosts/servers that's problematic since the port is not known in advance.
     
  3. Ozgekocaoglu

    Ozgekocaoglu

    Joined:
    Feb 13, 2019
    Posts:
    38
    Firstly, thanks for your reply.

    No, host and client are not on the same network. I'm sorry i don't have much information about networking. I guess i have to make port forwarding.

    In my case, we are making a brawler multiplayer game therefore we don't have ping tolarence. In the theory, connections should be p2p but without server. That's why, i am not using relay.

    Can relay ensure my clients connect with each other without server and with low ping?

    -

    I'm scheduling the updates like that but i'm not sure it working:

    Code (CSharp):
    1.   public async void Update()
    2.   {
    3.     if(!currentLobby.isNotNull()) return;
    4.     if(!loggedPlayer.isNotNull()) return;
    5.    
    6.     if(currentLobby.HostId == loggedPlayer.Id){
    7.         HostDriver.ScheduleUpdate().Complete();
    8.        
    9.         NetworkConnection c;
    10.         while ((c = HostDriver.Accept()) != default(NetworkConnection))
    11.         {
    12.             hostConnections.Add(c);
    13.             Debug.Log("Accepted a connection");
    14.         }
    15.  
    16.        
    17.     DataStreamReader stream;
    18.     for (int i = 0; i < hostConnections.Length; i++)
    19.     {
    20.         NetworkEvent.Type cmd;
    21.         while ((cmd = HostDriver.PopEventForConnection(hostConnections[i], out stream)) != NetworkEvent.Type.Empty)
    22.         {
    23.  
    24.             if(cmd == NetworkEvent.Type.Connect){
    25.                 Debug.Log("Server connected");
    26.             }
    27.             else if (cmd == NetworkEvent.Type.Disconnect)
    28.             {
    29.                 Debug.Log("Client disconnected from server");
    30.                 hostConnections[i] = default(NetworkConnection);
    31.             }
    32.         }
    33.     }
    34.     }
    35.     else{
    36.             PlayerDriver.ScheduleUpdate().Complete();
    37.     }
    38.   }
    39.  
     
    Last edited: May 26, 2022
  4. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    If players are behind different networks, then you'll need either to pass all traffic through a relay server, or have some service to broker connections between the peers (you should look into "NAT punch-through" solutions; a typical one being STUN).

    Relay servers can add latency, but they're more reliable and much easier to use than NAT traversal techniques. Unfortunately for your use case, they may also enforce one player being the host, with all other clients communicating with it. (Although I'd argue that's probably a good thing. Synchronizing state in a fully P2P environment is a very hard problem to solve.)

    Assuming the condition does get
    PlayerDriver
    updated for clients, your code looks good to me. Have you tried it locally with both host and client on the same machine?
     
  5. Ozgekocaoglu

    Ozgekocaoglu

    Joined:
    Feb 13, 2019
    Posts:
    38
    I'm trying to implement an upnp port forwarder for this. It will probably looks like that:

    https://github.com/Codeh4ck/UPnP-Port-Forwarder


    I think it's a good example to learn how it works.

    I consider that it must be an upnp because my protocol will be UTP. I know i have to make bunch of configuration but what is the alternative except relay services?

    And yes, i've tried also locally at the same machine and it did not work again. Maybe problem is not the ports, it about the my code. I'm sure player drivers and server driver updates themselves. I guess i need a some kind of trigger for lobby connections. After someone login the lobby, if i can trigger my server, i can update my also player drivers? I've checked out the sample project, i know relay can do that but lobby doesn't have On_Join or On_Exit events.
     
  6. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    NAT punch-through would be an alternative to a relay service or port forwarding. It still requires a server to broker the connection between your devices, but once the connection is established, the devices will be talking to each other directly.

    Regarding having issues connecting players on the same machine, have you gotten the simple example in the documentation working? That would rule out any issues with UTP on your machine. Also, how do you know which port to connect to? From the code above, the host is binding to an ephemeral port.
     
  7. Ozgekocaoglu

    Ozgekocaoglu

    Joined:
    Feb 13, 2019
    Posts:
    38
    All right, i will check out NAT punch-through for port forwarding.

    Yes simple example in the documentation is working. But in the simple example i'm adding server and client script as component to my game object. But in my case, i only have one script and that handles both. I expect if i start server from my local machine, other client can listen. I'm binding to 9000 port number. Should i open port as perminatly?
     
  8. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    I'm not sure I understand your last question here.

    You should only listen on the driver that is going to accept connections (the host/server), and only that driver should bind to a well-known port. Client drivers should bind to a different port (or to port 0 to get an ephemeral port).
     
  9. Ozgekocaoglu

    Ozgekocaoglu

    Joined:
    Feb 13, 2019
    Posts:
    38
    I got it. Thanks a lot for your helps!