3
\$\begingroup\$

I'm calling Connect() for the first time I connect, I am then checking if the connection is still connected every 10 seconds if it is not connected it attempts to re-connect.

public class ClientHandler : ISocketHandler, IDisposable
{
    private readonly Socket _socket;
    private readonly byte[] _buffer;

    private ISession _session;
    private bool _hasConnected;

    public ClientHandler()
    {
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _buffer = new byte[8192];

        var timer = new Timer
        {
            Interval = 30000
        };

        timer.Elapsed += ElapsedTimer;
        timer.Enabled = true;
    }

    private void ElapsedTimer(object sender, ElapsedEventArgs e)
    {
        if (!CoreUtilities.IsConnected(_socket))
        {
            Connect();
        }
    }

    public void Connect()
    {
        var host = _session.ConfigHandler["server.host"];
        var port = short.Parse(_session.ConfigHandler["server.port"]);

        try
        {
            _socket.Connect(new IPEndPoint(IPAddress.Parse(host), port));
            _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);

            CoreUtilities.LogToConsole("Successfully established a connection to the server.");
        }
        catch (SocketException)
        {
            CoreUtilities.LogToConsole("Failed to establish a connection with the server.");
        }
        finally
        {
            if (_socket.Connected && CoreUtilities.IsConnected(_socket))
            {
                Send(new SocketPasswordComposer(_session.ConfigHandler["server.password"]));
            }
        }
    }
}

Here is the method I use to determine if the socket is fully connected to the server.

public static bool IsConnected(Socket socket)
{
    return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}

I feel that what I have coded above could be improved a lot.

\$\endgroup\$
  • 2
    \$\begingroup\$ What actually makes you feeling wrong about what you're doing there? \$\endgroup\$ – πάντα ῥεῖ Apr 17 '18 at 1:01
  • \$\begingroup\$ Not wrong, just not very practical. It feels like I'm doing a lot of work to delegate a small job which can be done much easier, although I can't see a better way as of now. \$\endgroup\$ – user692959239 Apr 17 '18 at 1:07
  • 2
    \$\begingroup\$ Please post all code of this class. \$\endgroup\$ – Heslacher Apr 17 '18 at 4:20
4
\$\begingroup\$

I see there is problem in the usage to Timer. Consider the case if the server takes more time (> 5000) to connect, than the timer interval specified (3000) specified. Then the timer elapsed event will fire again though the first elapsed event is not yet finished. In order to avoid this stop the timer in the begin of Connect() and start the timer in end of Connect().

\$\endgroup\$
0
\$\begingroup\$

I would like to share a sample solution based on following aspects

  1. Client is only responsible for Sending, Receiving, Connecting and disconnecting from Server.
  2. Wrapper class is responsible for state management of Client, like initialization, dispose, re-connection, processing the received data, forwarding data to client, decision based on the connection state of Client with server

In this way you could able to reuse the Client and add have different wrapper classes based on the functionality like (Active-Active client, Active - Passive client, Broad cast clients)...

I would like to share the abstract code (doesn't have any DI or pattern implementation), you can modify according to your requirement.

    public enum LinkStatus
    {
       Connected,
       Disconnected
    }

MessageEventArgs Event args raised by client, during the sending and reception of message to server, which includes the data and status.

    public class MessageEventArgs : EventArgs
    {
        public byte[] Message { get; private set; }
        public bool Result { get; private set; }
        public int Index { get; private set; }
        public MessageEventArgs(int pIndex, byte[] pData, bool pResult)
        {
            Message = pData;
            Result = pResult;
            Index = pIndex;
        }
    }

ServerEventArgs Event args class holding server details

    public class ServerEventArgs : EventArgs
    {
        public IPAddress IP { get; private set; }
        public int Port { get; private set; }
        public int Index { get; private set; }
        public ServerEventArgs(int pIndex, IPEndPoint pServerEndPoint)
        {
            this.IP = pServerEndPoint.Address;
            this.Port = pServerEndPoint.Port;
            this.Index = pIndex;
        }
    }

MyTcpClient class is shown below, which will do following tasks

a. Expose methods to start and stop communication with server

b. Expose Connection Link status

c. Raise events like MessageSentSuccessfully, MessageSendingFailed, Disconnected, ConnectingSucceeded, MessageReceived.

    public class MyTcpClient
    {
        private int mIndex;
        private LinkStatus mConnectionStatus = LinkStatus.Disconnected;
        private Socket mClientSocket = null;
        private NetworkStream mNetworkStream = null;
        private BackgroundWorker mBwReceiver;
        private IPEndPoint mServerEP = null;
        private IPEndPoint mClientEP = null;
        private Semaphore mSendSemaphore;
        private int mConnectionSleepTime = 5000;

        public MyTcpClient(int pIndex, string pServerAddress, int pPortNumber)
        {
            this.mIndex = pIndex;
            this.mSendSemaphore = new Semaphore(1, 1);
            IPAddress address = IPAddress.Parse(pServerAddress);
            IPEndPoint endPoint = new IPEndPoint(address, pPortNumber);
            this.mServerEP = endPoint;
            this.mClientEP = new IPEndPoint(IPAddress.Any, 0);
            this.mSendSemaphore = new Semaphore(1, 1);
        }

        public event EventHandler<MessageEventArgs> MessageReceived;
        public event EventHandler<MessageEventArgs> MessageSent;
        public event EventHandler<MessageEventArgs> MessageSendingFailed;
        public event EventHandler<ServerEventArgs> Disconnected;
        public event EventHandler<ServerEventArgs> ConnectingSucceeded;

        public LinkStatus ConnectionStatus
        {
            get
            {
                if (this.mConnectionStatus == LinkStatus.Connected)
                {
                    bool result = false;
                    try
                    {
                        if (this.mClientSocket != null)
                        {
                            result = !(this.mClientSocket.Poll(1, SelectMode.SelectRead) && this.mClientSocket.Available == 0);
                        }
                    }
                    catch
                    {
                    }
                    if (result)
                    {
                        this.mConnectionStatus = LinkStatus.Connected;
                    }
                    else
                    {
                        this.mConnectionStatus = LinkStatus.Disconnected;
                    }
                }
                else
                {
                    this.mConnectionStatus = LinkStatus.Disconnected;
                }

                return this.mConnectionStatus;
            }
        }

        public void ConnectToServer(int pSleepingInterval)
        {
            this.mConnectionSleepTime = pSleepingInterval;
            BackgroundWorker bwConnector = new BackgroundWorker();
            bwConnector.DoWork += new DoWorkEventHandler(this.BwConnector_DoWork);
            bwConnector.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwConnector_RunWorkerCompleted);
            bwConnector.RunWorkerAsync();
        }


        public void SendCommand(byte[] pMessage)
        {
            {
                if (this.ConnectionStatus == LinkStatus.Connected)
                {
                    BackgroundWorker bwSendWorker = new BackgroundWorker();
                    bwSendWorker.DoWork += new DoWorkEventHandler(this.BwSendWorker_DoWork);
                    bwSendWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwSendWorker_RunWorkerCompleted);
                    bwSendWorker.WorkerSupportsCancellation = true;
                    bwSendWorker.RunWorkerAsync(pMessage);
                }
                else
                {
                    this.OnMessageSendingFailed(new MessageEventArgs(this.mIndex, pMessage, false));
                }
            }
        }

        public void DisconnectFromServer(bool pCanRaise)
        {
            try
            {
                if (this.ConnectionStatus == LinkStatus.Connected)
                {
                    try
                    {
                        this.mBwReceiver.CancelAsync();
                        this.mBwReceiver.Dispose();
                    }
                    catch
                    {
                    }
                }

                try
                {
                    this.mClientSocket.Shutdown(SocketShutdown.Both);
                    this.mClientSocket.Close();
                }
                catch
                {
                }
            }
            finally
            {
                this.mClientSocket = null;
            }

            this.SocketDisconnected(pCanRaise);
        }
        protected virtual void OnMessageReceived(MessageEventArgs e)
        {
            if (this.MessageReceived != null)
            {
                this.MessageReceived(this, e);
            }
        }

        protected virtual void OnMessageSent(MessageEventArgs e)
        {
            if (this.MessageSent != null)
            {
                this.MessageSent(this, e);
            }
        }

        protected virtual void OnMessageSendingFailed(MessageEventArgs e)
        {
            if (this.MessageSendingFailed != null)
            {
                this.MessageSendingFailed(this, e);
            }
        }

        protected virtual void OnServerDisconnected(ServerEventArgs e)
        {
            this.DisconnectFromServer(true);
        }

        protected virtual void OnConnectionSucceeded()
        {
            this.mConnectionStatus = LinkStatus.Connected;
            if (this.ConnectingSucceeded != null)
            {
                this.ConnectingSucceeded(this, new ServerEventArgs(this.mIndex, this.mServerEP));
            }

        }

        protected virtual void OnConnectingFailed()
        {
            this.DisconnectFromServer(true);
        }

        private void BwConnector_DoWork(object sender, DoWorkEventArgs e)
        {
            bool result = false;
            try
            {
                Thread.Sleep(this.mConnectionSleepTime);
                this.mClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.mClientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                this.mClientSocket.Bind(this.mClientEP);
                // thread gets block until it gets response for server
                this.mClientSocket.Connect(this.mServerEP);
                result = true;
            }
            //// catch generic exception
            catch
            {
                result = false;
            }

            e.Result = result;
        }

        private void BwConnector_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                //// raise connection fail event if client not connected to server
                if (!(bool)e.Result)
                {
                    this.OnConnectingFailed();
                }
                else
                {
                    this.mNetworkStream = new NetworkStream(this.mClientSocket);
                    this.mBwReceiver = new BackgroundWorker();
                    this.mBwReceiver.WorkerSupportsCancellation = true;
                    this.mBwReceiver.DoWork += new DoWorkEventHandler(this.BwReceiver_DoWork);
                    this.mBwReceiver.RunWorkerAsync();
                    this.OnConnectionSucceeded();
                }

                ((BackgroundWorker)sender).Dispose();
            }
            // catch generic exception if any thing happens, this is for safe
            catch
            {
            }
        }

        private void BwReceiver_DoWork(object sender, DoWorkEventArgs e)
        {
            while (this.ConnectionStatus == LinkStatus.Connected && (this.mNetworkStream != null))
            {
                try
                {
                    if (this.mNetworkStream.CanRead)
                    {
                        byte[] data = new byte[1024];
                        int noOfBytesRead = 0;
                        noOfBytesRead = this.mNetworkStream.Read(data, 0, data.Length);
                        if (noOfBytesRead > 0)
                        {
                            byte[] receivedData = new byte[noOfBytesRead];
                            Array.Copy(data, receivedData, receivedData.Length);
                            MessageEventArgs mArgs = new MessageEventArgs(this.mIndex, receivedData, true);
                            this.OnMessageReceived(mArgs);
                        }
                    }
                    else
                    {
                    }
                }
                catch
                {
                    break;
                }
            }

            this.OnServerDisconnected(new ServerEventArgs(this.mIndex, this.mServerEP));
        }

        private void BwSendWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            byte[] sendData = (byte[])e.Argument;
            MessageEventArgs args = null;
            //// check for connectivity
            if (this.ConnectionStatus == LinkStatus.Connected && sendData.Length > 0)
            {
                try
                {
                    this.mSendSemaphore.WaitOne();
                    this.mNetworkStream.Write(sendData, 0, sendData.Length);
                    this.mNetworkStream.Flush();
                    this.mSendSemaphore.Release();
                    args = new MessageEventArgs(this.mIndex, sendData, true);
                    e.Result = args;
                }
                //// catch generic exception, for safe
                catch
                {
                    args = new MessageEventArgs(this.mIndex, sendData, false);
                    this.mSendSemaphore.Release();
                    e.Result = args;
                }
            }
            else
            {
                args = new MessageEventArgs(this.mIndex, sendData, false);
                e.Result = args;
            }

        }

        private void BwSendWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                MessageEventArgs args = e.Result as MessageEventArgs;
                if (args != null)
                {
                    // raised when message is sent to server successfully
                    if ((!e.Cancelled && e.Error == null) && (bool)args.Result)
                    {
                        this.OnMessageSent(new MessageEventArgs(this.mIndex, args.Message, args.Result));
                    }
                    // raised when message sending is failed to server
                    else
                    {
                        this.OnMessageSendingFailed(new MessageEventArgs(this.mIndex, args.Message, args.Result));
                    }
                }
                else
                {
                    this.OnMessageSendingFailed(new MessageEventArgs(this.mIndex, null, false));
                }

                ((BackgroundWorker)sender).Dispose();
            }
            catch
            {

            }
        }

        public void SocketDisconnected(bool pCanRaise)
        {
            this.mConnectionStatus = LinkStatus.Disconnected;

            if (this.Disconnected != null && pCanRaise)
            {
                this.Disconnected(this, new ServerEventArgs(this.mIndex, this.mServerEP));
            }
        }

    }

MyTCPWrapper class is show below, which will do the following

a. Controls the life cycle of MyTCPClients, based on the connection status and message reception status.

b. Add multiple clients, to provide redundencies like (Active - Active, Active - Passive)...

    public class MyTcpWrapper
    {
        private MyTcpClient mTcpClinet = null;
        private bool mTransportConnectionStatus = false;
        private int mReconnect = 10;

        public int Reconnect
        {
            get
            {
                return this.mReconnect;
            }
            set
            {
                this.mReconnect = value;
            }
        }

        public bool TransportConnectionStatus
        {
            get
            {
                bool result = false;
                if(this.mTcpClinet != null)
                {
                    if(this.mTcpClinet.ConnectionStatus == LinkStatus.Connected)
                    {
                        result = true;
                    }
                }
                return result;
            }
        }

        public void InitializeTransport()
        {
            if (this.DeInitializeTransport())
            {
                this.mTcpClinet = new MyTcpClient(1, "serverAddress", 9000);
                this.mTcpClinet.ConnectingSucceeded += new EventHandler<ServerEventArgs>(Transport_ConnectingSucceeded);
                this.mTcpClinet.Disconnected += new EventHandler<ServerEventArgs>(Transport_Disconnected);
                this.mTcpClinet.MessageReceived += new EventHandler<MessageEventArgs>(Transport_MessageReceived);
                this.mTcpClinet.ConnectToServer(this.Reconnect);
            }
        }

        public bool DeInitializeTransport()
        {
            bool result = true;
            this.mReconnect = 0;
            try
            {
                if (this.mTcpClinet != null)
                {
                    this.mTcpClinet.MessageReceived -= this.Transport_MessageReceived;
                    this.mTcpClinet.Disconnected -= this.Transport_Disconnected;
                    this.mTcpClinet.ConnectingSucceeded -= this.Transport_ConnectingSucceeded;
                    this.mTcpClinet.DisconnectFromServer(false);
                }
            }
            catch
            {
            }
            finally
            {
                this.mTcpClinet = null;
            }
            return result;
        }

        public void SendData(byte[] pData)
        {            
            if (this.mTcpClinet.ConnectionStatus == LinkStatus.Connected)
            {
                this.mTcpClinet.SendCommand(pData);
            }
            else
            {
                // take actions accordingly.
            }
        }


        private void Transport_MessageReceived(object sender, MessageEventArgs e)
        {
        }
        private void Transport_Disconnected(object sender, ServerEventArgs e)
        {
            this.mTcpClinet.ConnectToServer(this.mReconnect);
        }
        private void Transport_ConnectingSucceeded(object sender, ServerEventArgs e)
        {
        }
        private void OnReceiveMessage(byte[] pData)
        {
        }
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.