3

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

user2144406
  • 180
  • 4
  • 10
  • Unplugging a cable isn't automatically going to close an existing connection, as neither side of that connection cares/knows about the physical state. That is what timeouts are for. – admdrew Jul 02 '14 at 18:27
  • Ok, but then when I write to the socket shouldn't it throw an exception? – user2144406 Jul 02 '14 at 18:33
  • Not unless the connection/socket has actually been closed. Do you get an exception thrown if you manually close the socket, or wait a long time for the connection to time out? – admdrew Jul 02 '14 at 18:35
  • If I use `client.Close();` I do get the exception, is there a way to tell if the socket is still connected then? – user2144406 Jul 02 '14 at 18:42

4 Answers4

0

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 ;)

Community
  • 1
  • 1
ziya
  • 5,916
  • 25
  • 32
0

You can only tell whether you are still connected or not by sending something and receiving something back. Just pushing bytes out into the network always works even if they go into a black hole. The Connected property is unreliable and almost all code using it is wrong.

Send something to the printer and receive a reply. Or, create a new connection (which internally will send and receive TCP control packets without data).

usr
  • 162,013
  • 33
  • 219
  • 345
  • Creating a new connection does work, however it takes a long time, so just to clarify there is no way of knowing if it is still connected without making a new connection? Is that what you are saying? Isn't the Write and Response code Sending and Receiving something back? – user2144406 Jul 02 '14 at 20:07
  • Yes, connected property is unreliable (thanks for that!) but sending an empty message should actually send a TCP packet over the wire (which is essentially what keep-alive is) and _should_ update the socket state. – ziya Jul 02 '14 at 20:08
  • Why would the TCP stack send anything when you issue an empty write? A waste of performance. And the other side would see no need to acknowledge zero bytes. Makes no sense. When you issue a send you are not generating packets. You are enqueuing bytes to be sent at the leisure of the stack. You must force an acknowledgement by sending and receiving data. – usr Jul 02 '14 at 20:23
  • Ok how to "force an acknowledgement"? – user2144406 Jul 02 '14 at 20:38
  • What else can I tell you? By sending and receiving something. Sorry if this is not what you want to hear. – usr Jul 02 '14 at 20:41
  • Ok what is the minimum number bytes I have to send? >2? 1? – user2144406 Jul 02 '14 at 21:06
  • @usr that's how TCP keep-alives are implemented. ACK is sent even though there is no data. – ziya Jul 02 '14 at 21:09
  • @MaxwellTroyMiltonKing you're right, there is protocol support for this. I don't know how to trigger this from an API standpoint and I couldn't find out how by researching. If you find out let me know! – usr Jul 02 '14 at 21:12
-1

When dealing with transport layers like the TCP protocol you need to use it like a 'Walkie-Talkie'. You need to decide when and for how long to talk. In other words the communication breaks down when both parties talk or listen at the same time.

Here is an example from the book C# in a Nutshell:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class TcpDemo
{
    static void Main()
    {
        new Thread(Server).Start();     // Run server method concurrently.
        Thread.Sleep(500);              // Give server time to start.
        Client();           
    }

    static void Client()
    {
        using (TcpClient client = new TcpClient("localhost", 51111 ))
        using(NetworkStream n = client.GetStream())
        {
            BinaryWriter w = new BinaryWriter(n);
            w.Write("Hello");
            w.Flush();
            Console.WriteLine(new BinaryReader(n).ReadString());
        }
    }

    static void Server()
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 51111);
        listener.Start();
        using(TcpClient c = listener.AcceptTcpClient())
        using(NetworkStream n = c.GetStream())
        {
            string msg = new BinaryReader(n).ReadString();
            BinaryWriter w = new BinaryWriter(n);
            w.Write(msg + " right back!");
            w.Flush();
        }
        listener.Stop();
    }
}
Pabinator
  • 1,479
  • 1
  • 20
  • 22
  • 1
    Ok I get your premise but do not see how that really applies, maybe you could explain that bit a little more – user2144406 Jul 02 '14 at 19:39
  • You have to follow this sequence. 1. Open the TcpClient; 2. Write; 3. Close; 4. Open the TcpLister; 5. Read; 6. Close. If you Open the TcpClient then you Write then you Listen is not going to work. In the example code the 'using' takes care of the Close that is way you don't see the close. – Pabinator Jul 02 '14 at 20:48
-1

I have same this propblem for reconnect. I write server in java and client in c# (unity) java-java throw exception ok java-c# : both of them throw exception in some case.

I have the best way for perfomance server I resolve by the way : write jar client and use ikvm export to dll (copy jar to ikvm/bin). Create library in c# and reference dll + ikvm.core.dll + .../manage/unityEngine.dll ==> copy Cshapr/bin/Debug to UnityProJect/Asset --> it run ok but speed over 37M to build

If you want to have a small client --> network with no reconnect

Alexan
  • 6,893
  • 13
  • 69
  • 89
Richard
  • 1
  • 1