15

I've been fighting with one problem for a whole 3 days I can't find any solution, please help :)
I work with Visual Studio 2010 and C# language.

I have a device working like a server, that sends some data in a very irregular periods of time (not possible to define any read timeout).
I wrote a TCP client to connect to that server and read data. It works OK, however when something is wrong with the network and server becomes unavailable (e.g. when I plug out the network cable from my computer), it takes about 10 seconds for application to "notice" there is no connection to the server and throw an exception. (I don't know why exactly 10 seconds? Where it's defined? Can I change it?)
I want to react faster - let say after one second after connection broken.
Googling for answer however doesn't provide me any working solution.

The test code is below, I try to make it on 2 threads: one is reading data, the second one is looking for connection status and should alarm me when it's broken. It's not working neither for TCPClient nor Socket class. I've tried to read / write some data with tcpClient.SendTimeout = 100; and stream.WriteTimeout = 100; but it doesn't seem to work.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;

namespace TCPClient
{
    class Program
    {
        static volatile bool _continue = true;
        static TcpClient tcpClient;
        static NetworkStream stream;

        static void Main(string[] args)
        {
            try
            {
                //client thread - listen from server
                Thread tcpListenThread = new Thread(TcpListenThread);
                tcpListenThread.Start();

                //connection checking thread
                Thread keepAliveThread = new Thread(KeepAliveThread);
                keepAliveThread.Start();

                while (_continue)
                {
                    if (Console.ReadLine() == "q")
                    {
                        _continue = false;
                    }
                }

                keepAliveThread.Join(2000);
                if (keepAliveThread.IsAlive)
                {
                    Console.WriteLine("Thread keepAlive has been aborted...");
                    keepAliveThread.Abort();
                }

                tcpListenThread.Join(2000);
                if (tcpListenThread.IsAlive)
                {
                    Console.WriteLine("Listen thread has been aborted...");
                    tcpListenThread.Abort();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("\n" + ex.Message);
            }

            Console.WriteLine("\nHit any key to quit...");
            Console.Read();
        }

        private static void TcpListenThread()
        {
            string server = "172.20.30.40";
            int port = 3000;

            try
            {
                using (tcpClient = new TcpClient())
                {
                    tcpClient.Connect(server, port);

                    if (tcpClient.Connected)
                        Console.WriteLine("Successfully connected to server");

                    using (stream = tcpClient.GetStream())
                    {

                        while (_continue)
                        {
                            Byte[] data = new Byte[1024];
                            Int32 bytes = stream.Read(data, 0, data.Length);
                            string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);

                            Console.WriteLine("Received: {0}, responseData);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Listen thread exception! " + ex.Message);
            }
        }


        private static void KeepAliveThread()
        {
            while (_continue)
            {
                if (tcpClient != null)
                {
                    try
                    {
                        //...what to put here to check or throw exception when server is not available??...
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Disconnected...");
                    }
                }

                Thread.Sleep(1000);  //test for connection every 1000ms
            }
        }
    }
}

Edit:
@carsten's answer: although it looks promising, this solution do not work...
I made the simplest test application for that:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;

namespace TCPClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string server = "172.20.30.40";
                int port = 3000;

                using (TcpClient tcpClient = new TcpClient())
                {
                    tcpClient.Connect(server, port);

                    int i = 0;
                    while (true)
                    {
                        // This is how you can determine whether a socket is still connected.
                        bool blockingState = tcpClient.Client.Blocking;
                        try
                        {
                            byte[] tmp = new byte[1];

                            tcpClient.Client.Blocking = false;
                            tcpClient.Client.Send(tmp, 0, 0);
                            Console.WriteLine("Connected!");
                        }
                        catch (SocketException e)
                        {
                            // 10035 == WSAEWOULDBLOCK
                            if (e.NativeErrorCode.Equals(10035))
                                Console.WriteLine("Still Connected, but the Send would block");
                            else
                            {
                                Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
                            }
                        }
                        finally
                        {
                            tcpClient.Client.Blocking = blockingState;
                        }

                        Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++);

                        Thread.Sleep(1000);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Global exception: {0}", ex.Message);
            }
        }
    }
}

The results are following, it displays:

Connected!
Connected: True

plus my order number every one second. When I disconnect network cable, it takes 8 seconds to start printing:

Disonnected: error code 10054!
Connected: False

so by 8 seconds I'm not aware that the connection is lost. It looks like pinging is the best option here, yet I'll test another solutions.

mj82
  • 4,933
  • 6
  • 29
  • 39
  • Possible duplicate: [How can I check whether a (TCP) socket is (dis)connected in C#?](http://stackoverflow.com/questions/515458/how-can-i-check-whether-a-tcp-socket-is-disconnected-in-c) – ladenedge Oct 04 '11 at 15:45

6 Answers6

20

I think this is a question that often comes around. This might be why MSDN docs really give a good answer to this - see Socket.Connected

Quote from there:

The Connected property gets the connection state of the Socket as of the last I/O operation. When it returns false, the Socket was either never connected, or is no longer connected.

The value of the Connected property reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection, make a nonblocking, zero-byte Send call. If the call returns successfully or throws a WAEWOULDBLOCK error code (10035), then the socket is still connected; otherwise, the socket is no longer connected.

with this sample code:

// .Connect throws an exception if unsuccessful
client.Connect(anEndPoint);

// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
    byte [] tmp = new byte[1];

    client.Blocking = false;
    client.Send(tmp, 0, 0);
    Console.WriteLine("Connected!");
}
catch (SocketException e) 
{
    // 10035 == WSAEWOULDBLOCK
    if (e.NativeErrorCode.Equals(10035))
        Console.WriteLine("Still Connected, but the Send would block");
    else
    {
        Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
    }
}
finally
{
    client.Blocking = blockingState;
}

 Console.WriteLine("Connected: {0}", client.Connected);

and the straight forward motification for an extension-method:

public static bool IsConnected(this Socket client)
{
   // This is how you can determine whether a socket is still connected.
   bool blockingState = client.Blocking;
   try
   {
       byte [] tmp = new byte[1];

       client.Blocking = false;
       client.Send(tmp, 0, 0);
       return true;
   }
   catch (SocketException e) 
   {
       // 10035 == WSAEWOULDBLOCK
       if (e.NativeErrorCode.Equals(10035))
           return true;
       else
       {
           return false;
       }
   }
   finally
   {
       client.Blocking = blockingState;
   }
}
Carsten
  • 49,407
  • 9
  • 85
  • 111
  • If I read this right, does that mean (basically) that by doing `Send(new byte[0])` the Send-method waits for the underlying `ACK` to arrive as a response to the package sent? – Alxandr Oct 05 '11 at 00:19
  • 1
    @Carsten König: Thank you for answer. It looks promising, but I'm afraid do not work. I've edited my post to explain the problem with this solution. – mj82 Oct 05 '11 at 08:48
10

This is a very old thread, but it's the first SO post that came up when I searched for this question and I found a more useful solution somewhere else, so I thought I'd post a link to it to help others in the future:

https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum=netfxnetcom&prof=required

ElFenix posted this answer that worked for me:

// Detect if client disconnected
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
  byte[] buff = new byte[1];
  if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
  {
    // Client disconnected
    bClosed = true;
  }
}
Ronan Boiteau
  • 8,035
  • 6
  • 32
  • 47
David
  • 3,810
  • 2
  • 31
  • 52
  • 2
    Funny that is still the same way we did in C 20 years ago (or more for the elders) – JB. With Monica. Jun 12 '18 at 15:32
  • 1
    According to microsoft you should use ioctlsocket instead of Peek https://support.microsoft.com/en-gb/help/192599/info-avoid-data-peeking-in-winsock This would be `tcp.Client.Available == 0` in c# – Giles Bathgate Jul 14 '19 at 18:18
7

Simple answer. You can't. Tcp is made in a way which doesn't allow this. However, the normal way to achieve this is to send ping's with shorter interval than messages. So, say, that whenever you get a message from the server, you start a clock that count down 1 min, then you send a "ping" command (if you haven't received a new message in between). If you don't receive a response to your "ping" within 30 seconds, you conclude that the connection is broken.

Theoretically, you should be able to do this on a package-level (tcp sends ACK which you should be able to check for), but I don't know if that's possible in C#, or any programming language for that matter, or if you need to do that in firmware/hardware.

Alxandr
  • 11,981
  • 10
  • 55
  • 93
  • Thank you for answer. So far it looks like pinging is the best option, however when digging for answer I've found some pages where it was pointed as a bad solution - don't know why. – mj82 Oct 05 '11 at 09:11
  • I think you're miss-understanding something, or I am (which is very much possible btw xD). As far as I understand, what you just updated your post to **is** pinging, just in a smarter way than actually doing it yourself. Plus, 8 seconds until you discover a disconnect doesn't sound bad at all. IRC-servers for instance, use up to 5 minutes between pings(!). – Alxandr Oct 06 '11 at 04:30
  • I've updated my post to show, that solution proposed by Carsten König do not work. It tries to do `tcpClient.Client.Send(...)` every second, so I've been expected that after cable disconnected it'll throw exception or something after one second - it do not. On the other hand, pinging "normal" way, with Ping class, will work. So, my update post is not equal to pinging :) – mj82 Oct 06 '11 at 09:31
1

Just in case anyone else needs something simple and effective.

This is the code I came up with

            while (true)
            {

                string s = null;
                DateTime readLag = DateTime.Now;
                try
                {
                    s = streamIn.ReadLine();
                }
                catch (Exception ex)
                {
                    SocketException sockEx = ex.InnerException as SocketException;
                    if (sockEx != null)
                    {
                        OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex);
                    }
                    else
                    {
                        OnDebug("Not SockEx :" + ex);
                    }
                }



                if (enableLog) OnDebug(s);
                if (s == null || s == "")
                {
                    if (readLag.AddSeconds(.9) > DateTime.Now)
                    {
                        break;
                    }
                }
                else
                {
                    CommandParser(s);
                }
            }

What I got was native error 10035 every time a read failed but would block.

When the connection was trashed, I instantly got a return with no data.

Setting the readLag.AddSeconds() to just below my read timeout would give me a pretty good idea that the time never elapsed, and I got no data. Which should not happen.

Soon as this criteria popped up, I just kick out of the loop and the thread ends.

Hope this helps anyone else out there.

The Lazy Coder
  • 10,822
  • 4
  • 47
  • 69
0

it will be correct and working if you type the following:

byte[] tmp = new byte[] {0};
...             
client.Send(tmp, 1, 0);
...
0

There is a socket option called SO_KEEPALIVE from the oginal unix implemetnation that has the TCP protocol send occasional probes to make sure the other end of the pipe is still listening. It can be set along with how often the probes are set via Socket.IOControl

user957902
  • 2,890
  • 11
  • 17
  • Eh, KEEPALIVEs are flaky at best. They can sometimes cause problems than they solve - I just can't remember why or what the source was - but use with care. – Jonathan Dickinson Oct 04 '11 at 15:48