0

I have a problem with connection handling in TCP Client socket.

This code should connect to localhost at 4444 port and listening for all incoming data from this TCP Server.

I need to write connection handling for this. For example if while attempting to connect server is not responding it should trying to connect again, or if connection is ready and after receiving some data TCP server will close the connection TCP client should try to reconnect again.

Can anyone help me with this problems

Here is what I have in this moment

using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;

public class TCPClientNew : MonoBehaviour {

    private Socket _clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
    private byte[] _recieveBuffer = new byte[8142];

    private void StartClient()
    {
        try
        {
            _clientSocket.Connect(new IPEndPoint(IPAddress.Loopback,4444));
        }
        catch(SocketException ex)
        {
            Debug.Log(ex.Message);

            // Try to reconnect ??  TODO
        }
        Debug.Log ("connected");

        _clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);

    }

    private void ReceiveCallback(IAsyncResult AR)
    {
        //Check how much bytes are recieved and call EndRecieve to finalize handshake
        int recieved = _clientSocket.EndReceive(AR);

        if(recieved <= 0)
            return;

        //Copy the recieved data into new buffer , to avoid null bytes
        byte[] recData = new byte[recieved];
        Buffer.BlockCopy(_recieveBuffer,0,recData,0,recieved);


        //Processing received data
        Debug.Log (System.Text.Encoding.ASCII.GetString(recData));



        //Start receiving again
        _clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
    }

    private void SendData(byte[] data)
    {
        SocketAsyncEventArgs socketAsyncData = new SocketAsyncEventArgs();
        socketAsyncData.SetBuffer(data,0,data.Length);
        _clientSocket.SendAsync(socketAsyncData);
    }

    void Start()
    {
        StartClient ();
    }
}
seek
  • 1,065
  • 16
  • 33

1 Answers1

1

What you want is a way to keep retrying the connection if it fails. And if there is an exception during a read, you want to see if we're still connect and if not then re-connect. I added a loop in the Connect() method to retry the connect after waiting for 1 second.

In the receive callback, I put a try/catch and if there is an exception I will go back to the Connect() method to retry the connection.

public class TCPClientNew
{

    private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    private byte[] _recieveBuffer = new byte[8142];

    private void Connect()
    {
        bool isConnected = false;

        // Keep trying to connect
        while (!isConnected)
        {
            try
            {
                _clientSocket.Connect(new IPEndPoint(IPAddress.Loopback, 4444));
                // If we got here without an exception we should be connected to the server
                isConnected = true;
            }
            catch (SocketException ex)
            {
                Debug.Log(ex.Message);

                // Wait 1 second before trying to connect again
                Thread.Sleep(1000);
            }
        }

        // We are now connected, start to receive
        _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
    }

    private void ReceiveCallback(IAsyncResult AR)
    {
        //Check how much bytes are recieved and call EndRecieve to finalize handshake
        try
        {
            int recieved = _clientSocket.EndReceive(AR);

            if (recieved <= 0)
                return;

            //Copy the recieved data into new buffer , to avoid null bytes
            byte[] recData = new byte[recieved];
            Buffer.BlockCopy(_recieveBuffer, 0, recData, 0, recieved);

            //Start receiving again
            _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
        }
        catch (SocketException ex)
        {
            Debug.Log(ex.Message);

            // If the socket connection was lost, we need to reconnect
            if (!_clientSocket.Connected)
            {
                Connect();
            }
            else
            {
                //Just a read error, we are still connected
                _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
            }
        }
    }

    private void SendData(byte[] data)
    {
        SocketAsyncEventArgs socketAsyncData = new SocketAsyncEventArgs();
        socketAsyncData.SetBuffer(data, 0, data.Length);
        _clientSocket.SendAsync(socketAsyncData);
    }
}
Jon
  • 3,230
  • 1
  • 16
  • 28
  • Uhm...a code-only answer, especially one of this length, can be difficult for future users (and potentially the asker) to follow. Could you please add some explanation of what you changed, preferably indicating where the OP went wrong, and what you did you fix it? – Serlite Feb 12 '16 at 20:32
  • @Serlite sure, problem. I just modified the OPs class by changing a few lines. I'll comment – Jon Feb 12 '16 at 20:39
  • @Mangist I tried your method by this solution freeze Unity if TCP Server is not responding. Is it possible? or I'm executing it in wrong way? – seek Feb 13 '16 at 00:40
  • Run the TcpClient in another thread. Don't run this in your UI thread. Use Thread.Start() or TaskFactory. – Jon Feb 13 '16 at 00:42
  • @Mangist This resolve my problem with freezing, but after one Connection refused, it show in log "Invalid arguments" and doesn't connect again. – seek Feb 13 '16 at 00:55
  • @Mangist I resolve this problem with "Invalid arguments" adding _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); just before _clientSocket.Connect. Now trying to connect at the first time works perfectly. But if connection is lost retrying to connect doesn't work. I think that is because BeginReceive only works when something is actually sended from TCP Server, so it doesn't recognize that connection is lost. Did you know how to resolve this problem? – seek Feb 13 '16 at 13:20
  • You can add another background thread that checks _socket.IsConnected every few seconds and reconnects if it's dropped. BeginReceive should fail if the connection is lost, or at least timeout. – Jon Feb 13 '16 at 13:59
  • Timeout doesn't work on async sockets, and BeginReceive doesn't fail its freeze if connection will be lost - cable unplugged. – seek Feb 24 '16 at 14:02