Domanda

I am in a situation where I need to open up a stream between my listener and a TCPClient. Once the stream is open, the client will send a stream of messages, and I will send an ACK for each. If I don't get any messages for a set amount of time (5 minutes by default), I will disconnect from the server and go back to listening for a new connection.

The listener should only have one Client that attempts to communicate with it. The specs state that a stream should be opened and held until all messages have been sent, and this could mean that the device is connected for several days or longer. Unfortunately, the Client is third-party software, and I cannot guarantee that it will not close the connection after each message.

My question is, how can I tell whether the Client has disconnected. I've done my research and know that there are several answers for this question already, but they basically say I need to write to the stream, and if there is an exception, the connection has been closed. I wrote code to do this, but found out that some of the Clients are not set up to handle incoming messages that are not ACKs. So far, all I get are logged error messages, but I am afraid that this "ping" may cause one of the Clients to crash.

I could close the connection after each message, but at least one of the Clients will continue to try to send all messages in a series until they have all been received on a single stream, so it would not work with this solution.

I could set the NetworkStream.ReadTimeout to a small value like 1 second, but each time it times out there would be an Exception, and using Exceptions for flow-control is not good code (although I am doing a bit of it when pinging).

Below is a snippet of what I have now:

while (bytesRead == 0 && isConnected && isListening)
{
    if (stream.CanRead && stream.DataAvailable)
    bytesRead = stream.Read(receivedBytes, 0, BufferSize);
    else
    {
        if (++pingCount % 100 == 0)
        {
            //  Ping the server every 10 seconds to confirm the connection
            pingCount = 0;
            try
            {
                stream.Write(ping, 0, ping.Length);

                //  client.Connected gives the status of the connection at the last communication
                if (!client.Connected)
                    isConnected = false;
            }
            catch (Exception)
            {
                //  If an exception is thrown, then the connection is closed
                isConnected = false;
            }
        }
        Thread.Sleep(100);     //  Wait and try again
    }
}
È stato utile?

Soluzione

After a bit more searching, I was able to find out how to access the Socket from the TcpClient. After I had this, I was able to use Poll to confirm the connection was still open. My code is below:

try
{
    if (client.Client.Poll(1000, SelectMode.SelectRead) && client.Client.Available == 0)
    {
        isConnected = false;
    }
}
catch (Exception)
{
    //  If an exception is thrown, then the connection is closed
    isConnected = false;
}

Client.Poll returns true if the connection is closed, reset, terminated, pending, or there is data available. If you check Client.Available, and there is no data, then the connection must be closed. This is not foolproof, but works for my purposes, since I just go back to listening.

This solution came from zendar, and is located here.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top