1

I am using StreamSocket to connect to a socket server and I use a infinite loop to read data. Each piece of data ends with '\n\n'. The snippet is shown below. However, I can't know whether the socket is still alive, and every time the server shuts down the connection, ReadAsync() throws exception. How to be notified when the connection is closed?

public async Task<string> ReadData() {
    StreamReader reader = new StreamReader(socket.InputStream.AsStreamForRead());
    char[] ch = new char[65536];
    int i = 0;
    while (IsConnected) {
        await reader.ReadAsync(ch, i++, 1);
        if (i > 1 && ch[i - 1] == '\n' && ch[i - 2] == '\n') break;
    }
    return new StringBuilder().Append(ch, 0, i - 2).ToString();
}
Perqin
  • 755
  • 10
  • 27
  • Possible duplicate of [c# detecting tcp disconnect](https://stackoverflow.com/questions/15067014/c-sharp-detecting-tcp-disconnect) –  Apr 25 '18 at 16:30

2 Answers2

0

and every time the server shuts down the connection, ReadAsync() throws exception.

The exception is thrown because an ungraceful disconnect occurs. So make sure there is no any pending read or write operations exist on the socket before shutting down the connection on the server side, then there should be no exception. You can refer to the Remarks part of StreamSocket class for more details.

How to be notified when the connection is closed?

A graceful socket closure can be detected by the other side as a 0-length read. That is, it just acts like a regular end-of-stream. Here is a similar thread for your reference.

So you can use the following code to check if the connection is closed in your scenario:

// break when the connection is closed
if (ch[i - 1] == '\0') break;

Following is the entire method I have verified:

public async Task<string> ReadData()
{
    StreamReader reader = new StreamReader(socket.InputStream.AsStreamForRead());
    char[] ch = new char[65536];
    int i = 0;
    while (true)
    {
        await reader.ReadAsync(ch, i++, 1);
        // break when the connection is closed or data ends with '\n\n'
        if (ch[i - 1] == '\0' || (i > 1 && ch[i - 1] == '\n' && ch[i - 2] == '\n')) break;
    }

    if (ch[i - 1] == '\0')
    {
        // do something here when the connection is closed
    }
    else
    {
        return new StringBuilder().Append(ch, 0, i - 2).ToString();
    }
}
Community
  • 1
  • 1
Jerry Li
  • 1,042
  • 9
  • 8
0

every time the server shuts down the connection, ReadAsync() throws exception.

This is to be expected for most servers. TCP/IP has a rather expensive four-part handshake to gracefully close a connection, but from the server's perspective, that's a long time to wait to reclaim those resources. So, a lot of servers just "slam" the connection shut, causing a SocketError.ConnectionReset error.

As I describe in my socket error handling blog post, if the communication state is such that you would consider a "graceful close" appropriate at that point, then you should also accept an exception with ConnectionReset at the same point. (In your case, the appropriate "close" point is if the message has completed with a \n\n).

How to be notified when the connection is closed?

You will either get a ConnectionReset exception (if it is abruptly closed), or your ReadAsync will return 0 (if it is gracefully closed).

However...

How to know whether the socket connection is still alive?

There is only one reliable way to detect whether a socket connection is still viable: write data to it. I cover this in detail in my blog post on the half-open problem.

Because of this rather interesting quirk in socket communications, code like while (IsConnected) is an anti-pattern. Instead, you need to have a regular polling loop that sends data to the other side based on a timer.

P.S. If at all possible, use SignalR instead of raw sockets. They do all the hard stuff for you.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810