36

I'm playing around with the TcpClient and I'm trying to figure out how to make the Connected property say false when a connection is dropped.

I tried doing

NetworkStream ns = client.GetStream();
ns.Write(new byte[1], 0, 0);

But it still will not show me if the TcpClient is disconnected. How would you go about this using a TcpClient?

Superdumbell
  • 4,439
  • 11
  • 40
  • 49

9 Answers9

55

I wouldn't recommend you to try write just for testing the socket. And don't relay on .NET's Connected property either.

If you want to know if the remote end point is still active, you can use TcpConnectionInformation:

TcpClient client = new TcpClient(host, port);

IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();

if (tcpConnections != null && tcpConnections.Length > 0)
{
    TcpState stateOfConnection = tcpConnections.First().State;
    if (stateOfConnection == TcpState.Established)
    {
        // Connection is OK
    }
    else 
    {
        // No active tcp Connection to hostName:port
    }

}
client.Close();

See Also:
TcpConnectionInformation on MSDN
IPGlobalProperties on MSDN
Description of TcpState states
Netstat on Wikipedia


And here it is as an extension method on TcpClient.

public static TcpState GetState(this TcpClient tcpClient)
{
  var foo = IPGlobalProperties.GetIPGlobalProperties()
    .GetActiveTcpConnections()
    .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
  return foo != null ? foo.State : TcpState.Unknown;
}
Peter Wone
  • 15,594
  • 12
  • 74
  • 106
uriel
  • 1,139
  • 1
  • 14
  • 23
  • 4
    This is an awesome answer. The only way you could improve it would be to present the test as an extension method of Socket that returns the socket state. – Peter Wone Aug 12 '14 at 09:45
  • 1
    Nice one. I really wonder if there is any faster way to do it though – Arsen Zahray Aug 04 '15 at 04:17
  • Thank you! Your answer is the first that has worked for me. I have tried various answers that used client.Client.Poll and they weren't working. – Blake Thingstad Nov 21 '16 at 15:27
  • Several issues: I just tried the extension method and received the following exception: "Sequence contains more than one matching element". The first bit of code works for me, after I also added a check that tcpConnections.Length==1 (if there are several results then we have an issue - how can we know that we're looking at the right result). I.e. the use of "SingleOrDefault" seems correct to me, which means it's required in the first bit of code. Also note that in the extension method there's no check for RemoteEndPoint equality. – Tom Mar 27 '17 at 15:53
  • 1
    Use `FirstOrDefault` instead of `SingleOrDefault` – David Moškoř Apr 08 '18 at 16:40
  • I'm getting `The method or operation is not implemented. at System.Net.NetworkInformation.UnixIPGlobalProperties.GetActiveTcpConnections()` or is it just me? – Pierre Feb 13 '19 at 11:12
  • 2
    change the line, .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)); – Wagner Pereira Feb 26 '19 at 21:03
  • This doesn't detect if you disable Wifi on the server machine. Tested with Win10 – JasonEdinburgh Jun 25 '19 at 11:49
  • For some reason, this is always returning `true` – mrid Sep 11 '19 at 09:08
8

As far as I know/remember there is no way to test if a socket is connected other than reading or writing to it.

I haven't used the TcpClient at all but the Socket class will return 0 from a call to Read if the remote end has been shutdown gracefully. If the remote end doesn't shutdown gracefully [I think] you get a timeout exception, can't remember the type sorry.

Using code like 'if(socket.Connected) { socket.Write(...) } creates a race condition. You're better off just calling socket.Write and handling the exceptions and/or disconnections.

David Basarab
  • 67,994
  • 42
  • 125
  • 155
Kepboy
  • 3,501
  • 2
  • 26
  • 40
  • Yeah. Socket layer shall managed using exceptions. The IOException thrown has the inner exception set to a SocketException, which contains all information required to detect timeouts or closed sockets remotely. – Luca Aug 26 '10 at 09:14
5

The solution of Peter Wone and uriel is very nice. But you also need to check on the Remote Endpoint, since you can have multiple open connections to your Local Endpoint.

    public static TcpState GetState(this TcpClient tcpClient)
    {
        var foo = IPGlobalProperties.GetIPGlobalProperties()
          .GetActiveTcpConnections()
          .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
                             && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
          );

        return foo != null ? foo.State : TcpState.Unknown;
    }
frankhommers
  • 694
  • 7
  • 20
3

I have created this function and working for me to check if client is still connected with server.

/// <summary>
/// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
/// </summary>
/// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
public bool isClientConnected()
{
    IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();

    TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();

    foreach (TcpConnectionInformation c in tcpConnections)
    {
        TcpState stateOfConnection = c.State;

        if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
        {
            if (stateOfConnection == TcpState.Established)
            {
                return true;
            }
            else
            {
                return false;
            }

        }

    }

    return false;


}
user391318
  • 61
  • 6
2

@uriel's answer works great for me, but I needed to code it in C++/CLI, which was not entirely trivial. Here is the (roughly equivalent) C++/CLI code, with a few robustness checks added in for good measure.

using namespace System::Net::Sockets;
using namespace System::Net::NetworkInformation;

TcpState GetTcpConnectionState(TcpClient ^ tcpClient)
{
    TcpState tcpState = TcpState::Unknown;

    if (tcpClient != nullptr)
    {
        // Get all active TCP connections
        IPGlobalProperties ^ ipProperties = IPGlobalProperties::GetIPGlobalProperties();
        array<TcpConnectionInformation^> ^ tcpConnections = ipProperties->GetActiveTcpConnections();

        if ((tcpConnections != nullptr) && (tcpConnections->Length > 0))
        {
            // Get the end points of the TCP connection in question
            EndPoint ^ localEndPoint = tcpClient->Client->LocalEndPoint;
            EndPoint ^ remoteEndPoint = tcpClient->Client->RemoteEndPoint;

            // Run through all active TCP connections to locate TCP connection in question
            for (int i = 0; i < tcpConnections->Length; i++)
            {
                if ((tcpConnections[i]->LocalEndPoint->Equals(localEndPoint)) && (tcpConnections[i]->RemoteEndPoint->Equals(remoteEndPoint)))
                {
                    // Found active TCP connection in question
                    tcpState = tcpConnections[i]->State;
                    break;
                }
            }
        }
    }

    return tcpState;
}

bool TcpConnected(TcpClient ^ tcpClient)
{
    bool bTcpConnected = false;

    if (tcpClient != nullptr)
    {
        if (GetTcpConnectionState(tcpClient) == TcpState::Established)
        {
            bTcpConnected = true;
        }
    }
    return bTcpConnected;
}

Hopefully this will help somebody.

Rawk
  • 41
  • 2
1

As of 2019, in a cross-platform and async environment, I use the code below to continuosly check that the TCP channel is open. This check fires e.g. if the ethernet cable is pulled on my Windows machine, or if the Wifi is disabled on my Android device.

private async Task TestConnectionLoop()
{
    byte[] buffer = new byte[1];
    ArraySegment<byte> arraySegment = new ArraySegment<byte>(buffer, 0, 0);
    SocketFlags flags = SocketFlags.None;

    while (!_cancellationSource.Token.IsCancellationRequested)
    {
        try
        {
            await _soc.SendAsync(arraySegment, flags);
            await Task.Delay(500);
        }
        catch (Exception e)
        {
            _cancellationSource.Cancel();

            // Others can listen to the Cancellation Token or you 
            // can do other actions here
        }
    }
}
Johan Franzén
  • 2,097
  • 1
  • 14
  • 12
1

Please note that I have found GSF.Communication wrapper for System.Net.Sockets.TcpClient to be helpful because it has a CurrentState property that indicates whether the socket is open/connected or closed/disconnected. You can find details on the NuGet package here:

https://github.com/GridProtectionAlliance/gsf

Here is how you could setup a simple TCP socket and test whether it is connected:

    GSF.Communication.TcpClient tcpClient;

    void TestTcpConnectivity() 
    {
        tcpClient = new GSF.Communication.TcpClient();
        string myTCPServer = "localhost";
        string myTCPport = "8080";
        tcpClient.MaxConnectionAttempts = 5;
        tcpClient.ConnectionAttempt += s_client_ConnectionAttempt;
        tcpClient.ReceiveDataComplete += s_client_ReceiveDataComplete;
        tcpClient.ConnectionException += s_client_ConnectionException;
        tcpClient.ConnectionEstablished += s_client_ConnectionEstablished;
        tcpClient.ConnectionTerminated += s_client_ConnectionTerminated;
        
        tcpClient.ConnectionString = "Server=" + myTCPServer + ":" + myTCPport;
        tcpClient.Initialize();
        tcpClient.Connect();        

        Thread.Sleep(250);
        
        if (tcpClient.CurrentState == ClientState.Connected)
        {
            Debug.WriteLine("Socket is connected");
            // Do more stuff 
        } 
        else if (tcpClient.CurrentState == ClientState.Disconnected)
        {
            Debug.WriteLine(@"Socket didn't connect");
            // Do other stuff or try again to connect 
        }
    }
    
    void s_client_ConnectionAttempt(object sender, EventArgs e)
    {
        Debug.WriteLine("Client is connecting to server.");
    }

    void s_client_ConnectionException(object sender, EventArgs e)
    {
        Debug.WriteLine("Client exception - {0}.", e.Argument.Message);
    }

    void s_client_ConnectionEstablished(object sender, EventArgs e)
    {
        Debug.WriteLine("Client connected to server.");
    }

    void s_client_ConnectionTerminated(object sender, EventArgs e)
    {
        Debug.WriteLine("Client disconnected from server.");
    }

    void s_client_ReceiveDataComplete(object sender, GSF.EventArgs<byte[], int> e)
    {
        Debug.WriteLine(string.Format("Received data - {0}.", tcpClient.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
    }       
user8128167
  • 5,380
  • 6
  • 53
  • 70
0

In my case, I was sending some command to a server (running in a virtual machine on the same computer) and waiting for the response. However, if the server stopped unexpectedly while waiting, I did not get any notification. I tried the possibilities proposed by the other posters, but neither did work (it always said that the server is still connected). For me, the only thing that is working is to write 0 bytes to the stream:

var client = new TcpClient();
//... open the client

var stream = client.GetStream();

//... send something to the client

byte[] empty = { 0 };
//wait for response from server
while (client.Available == 0)
{
    //throws a SocketException if the connection is closed by the server
    stream.Write(empty, 0, 0);
    Thread.Sleep(10);
}
LionAM
  • 896
  • 7
  • 24
0

Try this, it works for me

private void timer1_Tick(object sender, EventArgs e)
    {
        if (client.Client.Poll(0, SelectMode.SelectRead))
            {
                if (!client.Connected) sConnected = false;
                else
                {
                    byte[] b = new byte[1];
                    try
                    {
                        if (client.Client.Receive(b, SocketFlags.Peek) == 0)
                        {
                            // Client disconnected
                            sConnected = false;
                        }
                    }
                    catch { sConnected = false; }
                }
            }
        if (!sConnected)
        {
          //--Basically what you want to do afterwards
            timer1.Stop();
            client.Close();
            ReConnect();
        }

    }

i used Timer because, I wanted to check connection state at regular interval and not in a LOOP with Listening code [I felt it was slowing the sending-recieving process]

Akshay Vats
  • 160
  • 9