Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Is this the best way to send data between server and client?

Discussion in 'Scripting' started by KennethGames2020, Apr 3, 2020.

  1. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    I am working on a custom udp server system and was wondering where I can improve on.

    Here is some of the code:

    Code (CSharp):
    1.     //Some variables:
    2.     private bool connected = false;
    3.  
    4.     public InputField usernameInput, serverIPInput, serverPortInput, serverPasswordInput;
    5.     public Button connectButton;
    6.  
    7.     private int serverPort;
    8.     private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    9.     IPEndPoint serverEndPoint;
    10.     byte[] sendData = new byte[4096];
    11.     byte[] receiveData = new byte[4096];
    Code (CSharp):
    1.     private void OnConnectClick ()
    2.     {
    3.         if(usernameInput.text != "" && serverIPInput.text != "" && serverPortInput.text != "" && serverPasswordInput.text != "")
    4.         {
    5.             try
    6.             {
    7.                 serverPort = Int32.Parse(serverPortInput.text);
    8.                 try
    9.                 {
    10.                     serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIPInput.text), serverPort);
    11.                     Send("Connect/" + usernameInput.text + "/" + serverPasswordInput.text);
    12.                     Debug.Log("Attempting Connection...");
    13.                 }
    14.                 catch
    15.                 {
    16.                     Debug.Log("Invalid server IP!");
    17.                 }
    18.             }
    19.             catch
    20.             {
    21.                 Debug.Log("Port must be an integer!");
    22.             }
    23.         }
    24.     }
    Code (CSharp):
    1.     public void Send(string toSend)
    2.     {
    3.         sendData = Encoding.ASCII.GetBytes(toSend);
    4.         socket.SendTo(sendData, sendData.Length, SocketFlags.None, serverEndPoint);
    5.     }
    This is on the client menu UI. When the button is pressed, the server endpoint is set, and the client attempts to send connection information to the server.

    And here is how the server deals with the received data:

    Code (CSharp):
    1.     private void StartServer ()
    2.     {
    3.         serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    4.         try
    5.         {
    6.             serverSocket.Bind(new IPEndPoint(IPAddress.Parse(inputIP.text), Int32.Parse(inputPort.text)));
    7.         } catch
    8.         {
    9.             Debug.Log("Invalid IP or Port!");
    10.             return;
    11.         }
    12.         EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
    13.         Debug.Log("Server Started.");
    14.         serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref clientEP, ReceiveData, clientEP);
    15.  
    16.         serverName = inputName.text;
    17.         password = inputPassword.text;
    18.  
    19.         Debug.Log("Server Info: IP : " + IPAddress.Parse(((IPEndPoint)serverSocket.LocalEndPoint).Address.ToString()) + " : Port : " + ((IPEndPoint)serverSocket.LocalEndPoint).Port.ToString());
    20.  
    21.         LockServerInputFields();
    22.     }
    23.  
    24.     private void ReceiveData (IAsyncResult result)
    25.     {
    26.         try
    27.         {
    28.             EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
    29.             int dataLen = 0;
    30.             byte[] data = null;
    31.             try
    32.             {
    33.                 dataLen = serverSocket.EndReceiveFrom(result, ref clientEP);
    34.                 data = new byte[dataLen];
    35.                 Array.Copy(byteData, data, data.Length);
    36.             }
    37.             catch
    38.             {
    39.                 return;
    40.             }
    41.             finally
    42.             {
    43.                 EndPoint clientEP2 = new IPEndPoint(IPAddress.Any, 0);
    44.                 serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref clientEP2, ReceiveData, clientEP2);
    45.             }
    46.            
    47.             string received = Encoding.ASCII.GetString(data);
    48.             string[] splitReceived = received.Split('/');
    49.  
    50.             if(splitReceived[0] == "Connect")
    51.             {
    52.                 Debug.Log("A user is attempting to connect...");
    53.                 if(CanConnect(clientEP, splitReceived[1], splitReceived[2]))
    54.                 {
    55.                     Client newClient = new Client();
    56.                     newClient.clientEndpoint = clientEP;
    57.                     newClient.username = splitReceived[1];
    58.                     connectedClients.Add(newClient);
    59.                     Debug.Log(newClient.username + " has logged in.");
    60.                     SendConfirmConnect(newClient.clientEndpoint);
    61.  
    62.                     Debug.Log("User : " + newClient.username + " : has connected...");
    63.  
    64.                     //SpawnPlayer(newClient);
    65.                 }
    66.             }
    67.  
    68.         } catch
    69.         {
    70.             return;
    71.         }
    72.     }
    73.  
    74.     private bool CanConnect (EndPoint connectingClientEP, string newUsername, string passwordAttempt)
    75.     {
    76.         int existingClientEP = connectedClients.FindIndex(e => e.clientEndpoint == connectingClientEP);
    77.         int existingUsername = connectedClients.FindIndex(u => u.username == newUsername);
    78.         if(connectedClients.Count < 10 && passwordAttempt == password && existingClientEP == -1 && existingUsername == -1)
    79.         {
    80.             return true;
    81.         }
    82.         return false;
    83.     }
    Here is what is happening:

    When the user tries to connect to the server a string containing several pieces of information is sent to the server. The string is in segments divided by the "/" symbol.

    When the server receives the string, it splits it into its individual information pieces. In this case it is a connect request:

    Segment 0: Connect
    Segment 1: The player's username
    Segment 2: The password the player typed in to log into the server

    What I am trying to figure out is if there is a better way of communicating between server and client rather then just using strings.

    Thanks for any help.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,970
    Everything going over the wire is a string. :) In fact, the entire website, including graphics and everything, is all just strings. Honest!

    That said, slicing and dicing the strings yourself may be a tiny bit more performant, but also incredibly harder to maintain than using a formatted approach like JSON.

    With JSON you will get a complete object filled out on each side, and that way if you decide to reshape the JSON container, none of your code has to change, whereas with slicey-dicey yourself, you gotta debug and maintain that, and out here in the year 2020, that's not really where I would suggest spending engineering effort.
     
    KennethGames2020 likes this.
  3. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    Can you show me an example / documentation with JSON?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,970
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    If you're talking about web pages that is sort of true. But a socket connection its really byte arrays (though byte arrays and strings are almost the same things anyways).

    I'd just deal with everything in byte arrays though, except specifically string data (assuming you're taking the OP's approach and parsing everything yourself as it is). Otherwise you're doing silly things like converting a byte array to a string just to convert that string to a float, when you could just convert the byte array data directly to the float. If you're going to use something convenient like JSON, which is all strings, then yeah treat everything as strings. But if you're going to parse everything manually then just deal with the byte arrays and skip the extra string step. It should be more efficient packet size wise and less CPU usage.

    This jumped out at me. If you encode with ASCII there's a lot of characters you won't support. I'd recommend UTF8 instead. It is compatible with ASCII, still being just a single byte for all ASCII characters, but also supports the entire unicode character set for the instances you may need to send one of them (player may use one in their name or password, etc). Otherwise you'll have to filter all player input for compatibility with the ASCII character set.

    https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.utf8?view=netframework-4.8
     
    KennethGames2020 likes this.
  6. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    Gee thanks man I did not know about that.
     
  7. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    How are graphics converted to strings? I am implementing an inventory system and one of the variables for the item class is a sprite representing the item's icon in the inventory UI. How would I transport that across the internet?

    Edit: another variable in my item class is the 3d gameobject that represents the item such as a gun item's gun model. How do I send those?
     
    Last edited: Apr 4, 2020
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,970
    Binary data is generally MIME-64 encoded and sent over.

    Usually you do NOT do this for a game that has a remote server, unless you want to download content.

    Usually everything is already IN the game, and the server simply enables it.

    If you want to send binary data down, sending specific images is going to be a LOT of very hard fiddly work, and that goes even more for 3D objects. You would be responsible for recreating the objects, etc. MAJOR work... trust me.

    Instead use either Unity Asset Bundles or else Unity Addressibles and then it comes down over the wire all ready to go. Unity has already done all the hard work for you that way and there are thousands of tutorials on how to use Asset Bundles.
     
  9. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'm assuming these graphics and models aren't created by the players themselves, but are part of the build. In that case you don't send those graphics and models, you send some identifier. Some high level networking systems include a feature to identify networked enabled prefabs, but you can create such a system yourself. One simple example would be you throw all your networked prefabs into an array or list, where the collection will be identical on both server and client, and you just send the prefab's index number from that collection. Then the receiver of the index number can just look up which prefab is at that position in the collection and instantiate it.
     
    KennethGames2020 likes this.
  10. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    Thanks, but I thought of sending the name / id of the item along with the file path to the client so the client can use resources.load() to find its icon / model.
     
    Joe-Censored likes this.
  11. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    Question: Seeing how all data is transported via strings (now using json serialization), how do I prevent hacked clients from listening in on the data transported back and forth? I don't want the user to see position update data for other players.
     
  12. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,735
    Even AAA games don't usually try to hide this kind of data from clients. That's why hackers are able to create things like aimbots and wallhacks. They use other external tools to try to detect the hacking such as VAC which try to detect such modifications to the game.

    Theoretically though you would need to have the server simply not send any information to a client that you don't want it to see.
     
    KennethGames2020 likes this.
  13. KennethGames2020

    KennethGames2020

    Joined:
    Feb 26, 2020
    Posts:
    52
    Thanks for the fast response. I will keep that in mind.