TcpClient在异步读取过程中断开连接

3

我有几个关于结束tcp连接的问题。

  1. 客户端使用Tcp连接到我的服务器,使用 listener.BeginAcceptTcpClient(ConnectionEstabilishedCallback, null); 接受客户端后,我使用 networkStream.BeginRead(....) 开始读取。
    当我等待消息时,如果客户端断开连接会发生什么?(例如,它失去电源、网络等)
    我如何知道发生了什么?

  2. 如果成功读取后,我做一些事情,然后调用 networkStream.Close(); client.Close(); 客户端会看到什么?如何“优雅地”终止连接?

  3. 如果我正在等待读取(使用BeginRead),然后在另一个线程上关闭相同的流,会发生什么?

编辑以添加:我确实有一个ping-pong消息在客户端和服务器之间进行。这足够吗?如果我没有收到ping,就终止我的NetworkStream?肯定有更好的方法。


一个很好的关于套接字编程的参考资料是http://tangentsoft.net/wskfaq/newbie.html#normalclose - 它还详细阐述了客户端断开连接时发生的情况(NetworkStream是这些套接字的抽象)。 - C.Evenhuis
我对套接字不太了解,所以我真的需要一个简化并基于C#的解释。 - TDaver
3个回答

2

1- 如果客户端由于电缆拔出而断开连接,则在下一次读取或写入套接字时才会知道。还要注意,tcpClient.Connected属性值不可靠,其值取决于上一次通信; 因此,如果上一次通信成功,则其值为true,否则为false。有关更多信息,请查看 这个

2- 如果关闭网络流和客户端,则这是优雅地终止客户端。

3- 我不知道,请测试一下。

如果您意识到由于电缆拔出等原因而失去连接,则为了获得适当的IsConnected值,您必须在读取或写入tcp期间意识到连接丢失,因此需要在其操作周围放置try-catch以访问tcpclient成员....

使用该IsConnected属性来检查是否已连接tcpClient:

public static bool IsConnected
{
    get
    {
        try
        {
            //return _tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected;

            if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
            {

                /* As the documentation:
                    * When passing SelectMode.SelectRead as a parameter to the Poll method it will return 
                    * -either- true if Socket.Listen(Int32) has been called and a connection is pending;
                    * -or- true if data is available for reading; 
                    * -or- true if the connection has been closed, reset, or terminated; 
                    * otherwise, returns false
                    */

                // Detect if client disconnected
                if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
                {
                    byte[] buff = new byte[1];
                    if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
                    {
                        // Client disconnected
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }

                return true;
            }
            else
            {
                return false;
            }
        }
        catch
        {
            return false;
        }
    }
}

问题在于,在进行“IsConnected”检查之后,连接可能已经断开。 - jgauffin
返回翻译的文本:true,但与默认的“Connected”属性不同,它只有在被调用时才正确,然而由于这个原因,“连接可能在IsConnected检查之后立即断开”。当我遇到过这个问题时,我只是实现了一个定时器,在定义的时间间隔内检查“IsConnected”属性,如果失败,则调用自定义事件“ConnectionChanged”等。 - Jalal Said

2
当客户端在等待消息时断开连接会发生什么?(例如,它失去电源、互联网等)我怎样知道它已经断开连接了?
不同的断开原因会导致不同的情况。当你从socket.EndRead接收到0字节时,就会检测到优雅的断开连接。其他关闭将导致SocketException,要么是EndRead,要么是Send。
如果在成功读取后,我做了一些事情,然后调用networkStream.Close(); client.Close(); 客户端会看到什么?我如何“优雅地”终止连接?
Close会优雅地终止连接。
如果我正在等待读取(使用BeginRead),然后(在不同的线程上)关闭相同的流会发生什么?
您将收到一个异常。要么是ObjectDisposedException(如果您处理了客户端),要么是IOException。

-1
public class Example
{
    static NetworkStream stream;
    static TcpClient client;

    public static void Main(String[] args)

        String message = String.Empty;

        //TCP connection
 Recon: Console.WriteLine("Connecting...");
        Int32 port = 3333;
        try
        {
            client = new TcpClient("ip.ip.ip.ip", port); //Try to connect
        }
        catch
        {
            Console.WriteLine("Problem while connecting!");
            Thread.Sleep(10000); //Waiting 10s before reconnect
            goto Recon;
        }

        Console.WriteLine("Connection established!\n");

        stream = client.GetStream();

        while (true)
        {

            //Buffer
            byte[] received_bytes = new Byte[1024];
            message = "";

            Console.WriteLine("Waiting to receive message...\n");

            Int32 bytes = stream.Read(received_bytes, 0, received_bytes.Length);
            message = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(received_bytes, 0, bytes);

            if (bytes == 0) //If connection abort while reading
            {
                Console.WriteLine("Connection failed!");

                //Recconecting
                goto Recon;
            }

            Console.WriteLine("Received message: " + message);
        }
    }
}

请不要忽略异常对象或像那样循环。这可能会消耗大量CPU资源,对于更致命的异常情况更是如此。请在示例代码中使用英文变量名。 - jgauffin
如果您在实践中使用此代码,那么您肯定不会忽略异常。这只是一个概念,说明如何在接收消息时检测到断开连接。每个人都应该感激得到解决方案的概念,并根据自己的需求进行编辑。不要期望有人给您可以直接使用而无需修改的代码,只需复制粘贴即可。关于变量,如果bajti更改为bytes,对您来说并没有任何影响。我将在代码中更改此变量名称。 - Mega
例子应该遵循最佳实践,而不是一些快速而肮脏的解决方案,毕竟你正在教别人。由于你正在编写一个例子,所以它应该易于理解。使用本地变量名称会使其更加困难。每个人都应该感激得到解决方案的概念,但如果学习了不良实践并且以后必须重新做一切,那么可能不会感激。 - jgauffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接