August 10, 2019

Updated August 22, 2019

I’m currently developing a Unity multiplayer game that uses a Node.js server. In this blog post I’ll describe how the client communicates with the server via TCP.

It should be noted that I haven’t put much time into optimizing this solution and that I will probably switch to a network library later on. For now though, the focus is set on getting a prototype up and running and I figured I would post the current solution as a quick example of TCP communication between Unity and Node.js.

Using this example from Daniel Bierwirth as a starting point I was able to quickly get the network code on the Unity client up and running before modifying it for this project’s needs.

In the network script a new thread is created that will be dedicated to listening for data from the server.

private TcpClient tcpConnection ; void Awake ( ) { tcpConnection = new TcpClient ( SERVER_IP , SERVER_PORT ) ; clientReceiveThread = new Thread ( new ThreadStart ( DataReceiver ) ) { IsBackground = true } ; clientReceiveThread . Start ( ) ; }

The new data receiver thread will process the data and then send it back to the main thread. There’s a couple of different ways to communicate between threads, here we use a shared variable that can be accessed by both threads:

private ConcurrentQueue < string > serverMessageQueue = new ConcurrentQueue < string > ( ) ;

The variable must be thread safe so it’s advisable to use a class from the System.Collections.Concurrent namespace. The messages from the server will be enqueued to this variable in the receiver thread and then dequeued in the main thread. Below is the function that runs on the data receiver thread.

private void DataReceiver ( ) { try { NetworkStream stream ; using ( stream = tcpConnection . GetStream ( ) ) { byte [ ] buffer ; int numberOfBytesRead ; StringBuilder message ; while ( true ) { if ( stream . CanRead ) { buffer = new byte [ 1024 ] ; message = new StringBuilder ( ) ; numberOfBytesRead = 0 ; do { numberOfBytesRead = stream . Read ( buffer , 0 , buffer . Length ) ; message . Append ( Encoding . ASCII . GetString ( buffer , 0 , numberOfBytesRead ) ) ; } while ( stream . DataAvailable ) ; serverMessageQueue . Enqueue ( message . ToString ( ) ) ; } } } } catch ( SocketException e ) { Debug . Log ( $ "Socket exception: {e}" ) ; } }

An endless loop that listens for data, which is always a string in this project.

Meanwhile, the main thread checks the queue for new messages each update.

private void Update ( ) { if ( serverMessageQueue . TryDequeue ( out string strMsg ) ) { int index ; while ( ( index = strMsg . IndexOf ( "}{" ) ) != - 1 ) { ParseCommand ( strMsg . Substring ( 0 , index + 1 ) ) ; strMsg = strMsg . Substring ( index + 1 ) ; } ParseCommand ( strMsg ) ; } }

I found that the messages were combined sometimes if the server sent them rapidly. The information is sent as JSON so each message is checked to see if it contains any instances of “}{“, in which case the message is split into its proper components.

Sending messages to the server is done from the main thread.

private void SendMessageToServer ( string command , string data = null ) { var message = CreateNetworkMessage ( command , data ) ; var bytes = Encoding . ASCII . GetBytes ( message ) ; try { NetworkStream networkStream = tcpConnection . GetStream ( ) ; if ( networkStream . CanWrite ) { networkStream . Write ( bytes , 0 , bytes . Length ) ; } else { Debug . Log ( "Unable to send message" ) ; } } catch ( SocketException e ) { Debug . Log ( $ "Socket exception: {e}" ) ; } }

In the next post I’ll write about the server code.