Search code examples
c#socketstcptcpclient

C# TCPClient/Socket writing not throwing exception


I have many printers I am trying to connect to over tcp connections. I am trying to verify that my TcpClient is still connected to update a GUI. I am trying to write to a socket to make sure its still connected. I get no exception even if the cable is unplugged I tried all of the suggestions here MSDN_Fourm

I am receiving the expected exception after I try to check the printer statuses

psudo-code client is a TCPClient that has been connected previously

private bool FuntionPsudo(){
    try{
        if(client.Connected){
            byte[] buf = new byte[1];
            client.Client.Send(buf, 0,0);
            client.GetStream().Write(buf,0,0);
            if(client.Client.Receive(buf,SocketFlags.Peek)==0)
                return false;
            return true;
        }
    }
    catch(Exception){
        return false;
    }

    return false;
}

FuntionPsudo returns: true

cable unplugged

FuntionPsudo returns: true

FuntionPsudo returns: true

check printer status

FuntionPsudo returns: false

Thanks in advance for any help on why this might be happening and/or how to fix it


Solution

  • After several failed attempts I realised 'unplug-the-cable' type of connecting detection isn't that easy. At the same time I found that there are a couple of tricks you can do to check if the server has closed the connection, all without needing to send hearbeat kind of messages.

    Here is what I came up with that I could say it works most of the time (especially with cable disconnects it's not easy to figure out if connection is still up)

    static class SocketUtils
    {
        public static bool IsConnected(this Socket socket)
        {
            return IsSocketConnected(socket) && IsNetworkConnected(socket);
        }
    
        public static void KeepAlive(this Socket socket, int pollSeconds)
        {
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            SetIOControlKeepAlive(socket, (uint)(pollSeconds * 1000), 1);
        }
    
        static bool IsNetworkConnected(this Socket socket)
        {
            try
            {
                return socket.Send(new byte[0]) == 0;
            }
            catch (SocketException) { return false; }
        }
    
        static bool IsSocketConnected(this Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
    
        static void SetIOControlKeepAlive(Socket socket, uint time, uint interval)
        {
            var sizeOfUint = Marshal.SizeOf(time);
            var inOptionValues = new byte[sizeOfUint * 3];
            BitConverter.GetBytes((uint)(time == 0 ? 0UL : 1UL)).CopyTo(inOptionValues, 0);
            BitConverter.GetBytes(time).CopyTo(inOptionValues, sizeOfUint);
            BitConverter.GetBytes(interval).CopyTo(inOptionValues, sizeOfUint * 2);
    
            socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
        }
    }
    

    Here is how you can use it:

    var tcpClient = new TcpClient();
    
    tcpClient.Connect("192.168.2.20", 3000);
    
    // set this to a low value to detect cable disconnects early
    tcpClient.Client.KeepAlive(30); // 30 seconds
    
    Console.WriteLine("Connected..");
    while (true)
    {
        Thread.Sleep(500);
        Console.WriteLine(tcpClient.Client.IsConnected());
    }
    

    I must add that I shamelessly copied some code from Samuel's answer about checking client disconnects and Greg Dean's answer about setting keep-alive on the socket, so some credit should go to them as well ;)