关闭TcpClient和NetworkStream的问题

时间:2016-03-18 18:52:32

标签: c# tcpclient networkstream

目前我正在开发一个简单的客户端服务器聊天程序(希望通过C#进入客户端服务器通信事务)。它到目前为止工作,除了从服务器正确断开连接。

在客户端上,我在此处使用此代码来关闭连接:

client.Client.Disconnect(false); // client is the TcpClient
client.Close();

在服务器上有一个线程循环,等待来自客户端的消息:

private void StartChat()
{
    int requestCount = 0;
    byte[] bytesFrom = new byte[10025];
    string dataFromClient = null;
    string rCount = null;

    while (true)
    {
        try
        {
            requestCount++;

            NetworkStream stream = tcpClient.GetStream();

            int bufferSize = (int)tcpClient.ReceiveBufferSize;
            if (bufferSize > bytesFrom.Length)
            {
                bufferSize = bytesFrom.Length;
            }

            stream.Read(bytesFrom, 0, bufferSize);
            dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom);
            dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
            rCount = Convert.ToString(requestCount);

            string message = client.Name + " says: " + dataFromClient;
            program.Broadcast(message);

        }
        catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
        { 
            program.UserDisconnected(client);
            break;
        }
        catch(ArgumentOutOfRangeException ex)
        {
            Debug.WriteLine(ex.ToString());
            break;
        }
        catch(Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            break;
        }
    }

如果客户端与上面显示的代码断开连接,则该函数一直在获取流并产生这样的输出:

\0\0\0\0\0\0\0 [and so on]

在这种情况下,ArgumentOutOfRangeException将被抛出,因为没有$的索引。我添加了一个break来避免和无休止地执行循环。

令人惊讶的是,ObjectDisposedException不会被抛出。此外,System.IO.IOException不会被抛出,但它应该因为流被关闭所以连接被拒绝了。

如果我只关闭连接到服务器的客户端应用程序,服务器不会停止循环,它只会等待一个永远不会出现的流,因为客户端已断开连接。

那么如何检测客户端是否已断开连接或无法再访问?我关闭客户端连接的方式是关闭连接的正确方法吗?

感谢您的帮助!

更新

private void StartChat()
{
    int requestCount = 0;
    byte[] bytesFrom = new byte[10025];
    string dataFromClient = null;
    string rCount = null;

    while (true)
    {
        try
        {
            requestCount++;

            NetworkStream stream = tcpClient.GetStream();
            stream.ReadTimeout = 4000;

            int bufferSize = (int)tcpClient.ReceiveBufferSize;
            if (bufferSize > bytesFrom.Length)
            {
                bufferSize = bytesFrom.Length;
            }


            // Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown
            try
            {
                int bytesRead = stream.Read(bytesFrom, 0, bufferSize);
                stream.Flush();

                if (bytesRead == 0)
                {
                    throw new System.IO.IOException("Connection seems to be refused or closed.");
                }
            }
            catch (System.IO.IOException)
            {
                byte[] ping = System.Text.Encoding.UTF8.GetBytes("%");
                stream.WriteTimeout = 1;

                stream.Write(ping, 0, ping.Length);
                continue;
            }


            dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
            dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
            rCount = Convert.ToString(requestCount);

            string message = client.Name + " says: " + dataFromClient;
            program.Broadcast(message);

        }
        catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
        {
            Debug.WriteLine(ex.ToString());
            program.UserDisconnected(client);
            break;
        }
        catch(ArgumentOutOfRangeException ex)
        {
            Debug.WriteLine(ex.ToString());
        }
        catch(Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            break;
        }
    }
}

2 个答案:

答案 0 :(得分:3)

您应该检查stream.Read的返回值 - 它返回实际读取的字节数。

当客户端断开连接时,它将为0,当它读取数据时,读取的字节数通常小于缓冲区大小。

int bytes = stream.Read(bytesFrom, 0, bufferSize);
if (bytes == 0)
{
    // client has disconnected
    break;
}

dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom, 0, bytes);

在回应您最近的评论时,当客户端终止其连接时会发生以下三件事:

  • 客户端正确关闭连接,并且您收到0个字节
  • 发生了可检测到的事情,导致异常
  • 客户端“已消失”,但没有信号发送到您的结尾

在最后一种情况下,服务器仍然会像活动连接一样,直到它尝试发送数据,这显然会失败。这就是许多协议使用连接超时和/或保持活动机制的原因。

答案 1 :(得分:1)

int bytesRead = stream.Read(...); if(bytesRead == 0)//客户端断开连接