17

I have tcpclient object and i want to determine if it's connected or not. i use connected property of tcpclient but it returns the state of last operation. so its not useful.

then i use this code :

bool flag;
flag = (tcp.Client.Poll(10000, SelectMode.SelectWrite));

and

 if( tcp.Client.Poll( 0, SelectMode.SelectRead ) )
 {
   byte[] buff = new byte[1];
   if( tcp.Client.Receive( buff, SocketFlags.Peek ) == 0 )
   {
     flag = false;
   }
 }

but it does not work properly.

Any idea?


this is my code in server side :

   private ArrayList _ClientList = new ArrayList();

   public ClsServer(int port)
    {
        _TCPListener = new TcpListener(IPAddress.Any, port);
        _TCPListener.Start();

        Thread ListenThread = new Thread(new ThreadStart(ListenForClients));
        ListenThread.IsBackground = true;
        ListenThread.Start();
    }

    private void ListenForClients()
    {            
        while (true)
        {
            //blocks until a client has connected to the server
            TcpClient client = this._TCPListener.AcceptTcpClient();
            client.ReceiveTimeout = 0;

            //create a thread to handle communication with connected client
            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.IsBackground = true;
            clientThread.Start(client);
        }
    }

    private void HandleClientComm(object client)
    {
        try
        {
            TcpClient tcpClient = (TcpClient)client;               
            AddObject(tcpclient);

            int bytesRead;
            string message = "";
            byte[] RecievedPack = new byte[1024 * 1000];

            NetworkStream clientStream = tcpClient.GetStream();
            while (true)
            {
                bytesRead = 0;
                try
                {
                    ////blocks until a client sends a message
                    bytesRead = clientStream.Read(RecievedPack, 0, RecievedPack.Length);
                    int Len = BitConverter.ToInt32(RecievedPack, 0);
                    message = UTF8Encoding.UTF8.GetString(RecievedPack, 0, Len);
                }
                catch (Exception er)
                {
                    //When Client is disconnected
                    if (er.GetType() == typeof(IOException))
                    {
                        RemoveObject(client);
                        break;
                    }
                }                   
                //message has successfully been received                          
               // do something
            }

            RemoveObject(client);
        }
        catch(Exception e)
        {
           // RemoveObject(client);
        }
    }

    private void AddObject(object obj)
    {            
        int totalcount, index;
        totalcount = _ClientList.Count;
        index = 0;
        while (index < totalcount)
        {
            TcpClient alcobj = (TcpClient)_ClientList[index];
            try
            {
                if (IPAddress.Equals(((IPEndPoint)alcobj.Client.RemoteEndPoint).Address,
                   ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address))
                {
                    _ClientList.Remove(alcobj);
                    break;
                }
                index++;
            }
            catch (Exception er)
            {
                if (er.GetType() == typeof(ObjectDisposedException))
                    RemoveObject(alcobj);
            }
            finally
            {
                totalcount = _ClientList.Count;
            }
        }            
         _ClientList.Add(obj);             
    }

    private void RemoveObject(object obj)
    {            
        if (_ClientList.IndexOf(obj) > -1)
        {
            _ClientList.Remove(obj);
            SendClientState(IP, false);
        }         
    }

and this is the client side :

    public bool IsConnected
    {           
            try
            {
                if (_TcpClient != null && _TcpClient.Client != null && _TcpClient.Client.Connected)
                {
                    // Detect if client disconnected
                    if (_TcpClient.Client.Poll(0, SelectMode.SelectRead))
                    {
                        byte[] buff = new byte[1];
                        if (_TcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
                        {
                            // Client disconnected
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
    }

   private void clsClient()
   {
          if(!IsConnected()) 
          {
                  Connecttoserver()
           }
    }

    private void ConnectToServer()
    {
        try
        {
            NetworkStream _NetworkStream = _TcpClient.GetStream();
            byte[] _RecievedPack = new byte[1024 * 1000];
            string _Message = string.Empty;
            int _BytesRead;
            int _Length;

            while (true)
            {
                _BytesRead = _NetworkStream.Read(_RecievedPack, 0, _RecievedPack.Length);
                _Length = BitConverter.ToInt32(_RecievedPack, 0);
                _Message = UTF8Encoding.UTF8.GetString(_RecievedPack, 4, _Length);

                if (_BytesRead != 0)
                {
                    if (OnReceive != null)
                        // do something

                    _NetworkStream.Flush();
                }
            }
        }
        catch (Exception exp)
        {
            // do something 
        }
    }

in client side, IsConnected() always return false and try to connecttoserver, so the server listener always try to add the client in a list

stivlo
  • 77,013
  • 31
  • 135
  • 193
maryam mohammadi
  • 654
  • 3
  • 13
  • 27
  • Are you turning on `SocketOptionName.KeepAlive`? I think you must turn it on for your code to work. Anyway, you don't need to do this. The general solution is to consider socket being connected, and if the socket operation fails, gracefully handle the connection loss. But if you need to... – Cipi Aug 09 '11 at 09:25
  • Please read the [FAQ]. Stackoverflow is not a forum. – dandan78 Aug 10 '11 at 20:07
  • 1
    How is this a 'forum-like' question? In any case, the FAQ doesn't mention forums at all. – Tom W Oct 03 '11 at 17:50

4 Answers4

26

Use this code instead, I have tested it and using it in real production software:

public bool IsConnected
{
    get
    {
        try
        {
            if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
            {
               /* pear to the documentation on Poll:
                * When passing SelectMode.SelectRead as a parameter to the Poll method it will return 
                * -either- true if Socket.Listen(Int32) has been called and a connection is pending;
                * -or- true if data is available for reading; 
                * -or- true if the connection has been closed, reset, or terminated; 
                * otherwise, returns false
                */

                // Detect if client disconnected
                if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
                {
                    byte[] buff = new byte[1];
                    if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
                    {
                        // Client disconnected
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }

                return true;
            }
            else
            {
                return false;
            }
        }
        catch
        {
            return false;
        }
    }
}

Edit: However you can't rely on just checking the connection and if true proceed, because it returning the status of connection at time this property executed, for instance after you check IsConnected and it returns true, and while you are in the middle of communication, the connection maybe lost there! we just use it in the first place to reduce the probability of failure, So you have to wrap the whole communication in a try/catch and expect the connection to be lost at any time!

Jalal Said
  • 15,072
  • 7
  • 41
  • 66
  • Thanks for u'r answer but it does not work well for all my clients. some of them return connection state false, although they are realy connected, and some work exactly. – maryam mohammadi Aug 09 '11 at 09:08
  • the client connect to server once and then a timer call IsConnected, if it returns false client tries to connect again. the problem is that the function always return false for some clients!! – maryam mohammadi Aug 09 '11 at 09:30
  • maybe the problem in somewhere else, How are you checking each of clients? I think one method is to use a method here `public bool IsConnected(TcpClient _tcpClient)` and add a list of clients whenever a new client is added, then at the timer check `foreach (TcpClient client in _clientsArray)`.. Is that what you are doing? or each client has its own timer? – Jalal Said Aug 09 '11 at 09:35
  • each client has its own timer to check connection state because they try to connect to server. and the clients program are all the same. but i don't understand why just some of them always return false and try to connect again and again and again. never return true! – maryam mohammadi Aug 09 '11 at 09:49
  • Ok, do you mean you have a separate program "application" for each client? – Jalal Said Aug 09 '11 at 09:51
  • This is really odd, however if you can show us some code snippet that will helps to diagnose the problem more... – Jalal Said Aug 09 '11 at 11:18
  • @maryam : out of curiosity, did your problem solved? if so How? – Jalal Said Aug 13 '11 at 00:39
  • Your code is very helpful. Thank you very much for posting this. – joelc Mar 09 '15 at 22:13
  • @JalalAldeenSaa'd great piece of code. Well explained +1 – Moynul Feb 16 '16 at 19:29
5

I suggest not relaying on such methods. For my opinion, the best way is to implement some kind of keep-alive mechanism. Every X seconds, send a small message and wait for an answer. You're probably disconnected when: 1. You catch an exception when trying to send a keep-alive message (if you're the client side). 2. You don't get a keep-alive message/response for some period of time.

I also suggest not to count on the built-in TCP keep-alive, I found it very not-reliable.

Updated: Found a nice post for this matter: Post

Community
  • 1
  • 1
Lior Ohana
  • 3,277
  • 3
  • 31
  • 46
0

I agree with Lior Ohana because I had this problem with remote devices that were used GPRS Tcp connection. when device is turn off or disconnected, it did not alert to the sever. There for I used this code: I send Specific time to the clients

void AnalyzeForHandShaking(Socket socketin, string Message)

    {
        Socket handler = socketin;

        try
        {
            Message = Message.Trim();

            if (!string.IsNullOrEmpty(Message.Trim()))

               // if (Message.Equals("~"))
                {

                   // string Serial = getSerialFromSocket(socketin).Trim();
                    DateTime dt = DateTime.Now; 

                    if (handler!=null)
                    //if there is serial in hastable
                    if (!arrIPTimeHandShaking.ContainsKey(handler))
                    {
                        arrIPTimeHandShaking.Add(handler, dt);
                    }
                    else
                    {
                        arrIPTimeHandShaking[handler] = dt;
                    }
                }
        }
        catch
        {

        }
    }
0

The only way to know whether or not the other end of a socket connection is still connected is by catching the result of a read or write operation or possibly catching an exception.

For more information please refer to this StackOverflow question: Instantly detect client disconnection from server socket

Here is a small snippet of code that is simply using a Socket in non-blocking mode and connected to a server.

                try
                {
                    bytesRead = nambSok.Receive(message, 4096, SocketFlags.None);
                }
                catch (SocketException e)
                {
                    //a socket error has occured
                    switch (e.SocketErrorCode)
                    {
                        case System.Net.Sockets.SocketError.TimedOut:
                        case System.Net.Sockets.SocketError.WouldBlock:
                            if (doDisconnect == false)
                            {
                                continue;
                            }
                            break;

                        case System.Net.Sockets.SocketError.ConnectionReset:
                        case System.Net.Sockets.SocketError.ConnectionAborted:
                            isConnected = false;
                            break;
                    }
                }

                if (bytesRead > 0)
                {
                    /* do something with data */
                }

The "keep-alive" method Lior Ohana proposed is also a great idea. Force each client to "check in" every X seconds. The client can detect the server is gone if an exception occurs on the write, and the server knows the client has gone away if a "keep-alive" message hasn't been received in X seconds.

Community
  • 1
  • 1
Chimera
  • 5,552
  • 6
  • 41
  • 77