Search code examples
c#unity-game-enginetcptcpclient

TCP Client stalled until disconnected by the server


When trying to receive a string or file content (as string) via TCP I am stuck with an issue wher the receiving works in general, but the line

print("TCP -> Data received:\n" + file + "\n\n" + totalrecbytes + " Bytes");

is kind of stalled until I activelly disconnect from the server side. Than it works as expected.

I debugged and receiving the data inside the

while ((recBytes = netstream.Read(bytes, 0, bytes.Length)) > 0)

loop works just fine. It also ends the loop in the correct moment. But after that simply nothing happens. I get no errors, am not "trapped" in any loop but also do not get the expected output of

print("TCP -> Data received:\n" + file + "\n\n" + totalrecbytes + " Bytes");

until I disconnect from the server side. Than I see the expected output and the client is disconnected.

Here is the implementation (original source)

private Thread _tcpThread;
private TcpClient _socketConnection;

public void Connect()
{
    try
    {
        _tcpThread = new Thread(ReciveDataClient);
        _tcpThread.IsBackground = true;
        _tcpThread.Start();
    }
    catch (Exception e)
    {
        print(e.Message);
    }
}

private void ReciveDataClient()
{
    try
    {
        _socketConnection = new TcpClient("xxx.xxx.xxx.xxx", 54321);
        print(this, "TCP -> Connection Success!");
    }
    catch (Exception e)
    {
        print("connection error: " + e.Message)
        return;
    }

    try
    {
        var bytes = new byte[BUFFER_SIZE];

        while (_socketConnection.Connected)
        {
            if (_socketConnection.Available <= 0) continue;

            // Get a stream object for reading              
            var netstream = _socketConnection.GetStream();

            int totalrecbytes = 0;

            int recBytes;
            string file = "";

            // Read incomming stream into byte arrary.                  
            while ((recBytes = netstream.Read(bytes, 0, bytes.Length)) > 0)
            {
                var incommingData = new byte[recBytes];
                Array.Copy(bytes, 0, incommingData, 0, recBytes);
                // Convert byte array to string message.                        
                var serverMessage = Encoding.ASCII.GetString(incommingData);
                file += serverMessage;
                totalrecbytes += recBytes;
            }

            print("TCP -> Data received:\n" + file + "\n\n" + totalrecbytes + " Bytes");

            netstream.Close();
        }

        print("TCP -> connection was terminated by the server");
    }
    catch (Exception e)
    {
        print(e.Message)
        return;
    }
}

I would expect that I can maintain the connection alive but still receive the data correctly and communicate with the server on a persistent TCP connection.

What am I missing or doing wrong here?


The only workarround I could find so far is allways disconnect from the server side after sending data and in my code wrap the whole ReceiveDataClient in a while loop like

private void ReciveDataClient()
{
    while (true)
    {
        try
        {
            _socketConnection = new TcpClient(_server.ToString(), _server.Port);

            //...

in order to immediately start a new connection after the server sent some data and disconnected the client.


Solution

  • Thanks to the help of Damien_The_Unbeliever and Immersive I could figure it out. It really helps to read the docs from time to time especially if it is the first time you use something ^^

    NetworkStream.Read is a blocking call and as the doc states

    returns: The number of bytes read from the NetworkStream, or 0 if the socket is closed.

    so ofcourse the while loop actually never terminated.


    So adopting the example provided there worked for me except that if the server ended the connection I got another issue so instead of checking for _socketConnection.IsConnected I used the marked answer from this post so all together this works for me now

    private Thread _tcpThread;
    private TcpClient _socketConnection;
    
    public void Connect()
    {
        if(_socketConnection != null) return;
    
        try
        {
            _tcpThread = new Thread(ReciveDataClient);
            _tcpThread.IsBackground = true;
            _tcpThread.Start();
        }
        catch (Exception e)
        {
            print("TCP -> Thread error: " + e.Message);
        }
    }
    
    public void Disconnect()
    {
        if(_socketConnection = null) return;
    
        _tcpThread.Abort();
    }
    
    private void ReciveDataClient()
    {
        try
        {
            _socketConnection = new TcpClient("xxx.xxx.xxx.xxx", 54321);
            print(this, "TCP -> Connection Success!");
        }
        catch (Exception e)
        {
            print("TCP -> connection error: " + e.Message)
            return;
        }
    
        try
        {
            while(true)
            {
                // Get a stream object for reading              
                var netstream = _socketConnection.GetStream();
    
                //Check if still connected                
                if(_socketConnection.Client.Poll(0, SelectMode.SelectRead))
                {
                    byte[] buff = new byte[1];
                    if( _socketConnection.Client.Receive( buff, SocketFlags.Peek ) == 0 )
                    {
                        // Server disconnected or connection lost
                        break;
                    }
                }
    
                // Check to see if this NetworkStream is readable.
                if(myNetworkStream.CanRead)
                {
                    byte[] myReadBuffer = new byte[BUFFER_SIZE];
                    StringBuilder myCompleteMessage = new StringBuilder();
                    int numberOfBytesRead = 0;
                    int totalBytesReceived = 0;
    
                    // Incoming message may be larger than the buffer size.
                    do
                    {
                        numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
                        myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));    
                        totalBytesReceived += numberOfBytesRead;        
                    }
                    while(myNetworkStream.DataAvailable);
    
                    // Print out the received message to the console.
                    print("TCP -> Data received:\n" + myCompleteMessage.ToString() + "\n\n" + totalrecbytes + " Bytes");
                }
                else
                {
                    //Prevent a direct loop
                    Thread.Sleep(100);
                }          
            }
    
            print("TCP -> connection was terminated by the server");
        }
        catch(ThreadAbortException)
        {
            print("TCP -> Disconnected");
        }
        catch(Exception e)
        {
            print(e.Message)
        }
    
        // Clean up
        _socketConnection?.Close();
        _socketConnection?.Dispose();
        _socketConnection = null;
    }