如何正常关闭TCP连接而不例外?

时间:2009-08-26 18:43:50

标签: .net tcp

什么是模式和/或如何更改以下代码,以便可以从服务器或客户端正常关闭连接,而无需异常处理。

  1. TcpClient与Socket:我不喜欢使用TcpClient 客户类。我写了以下内容 代码试图演示 最简单的情况。我曾经 使用Socket&的SocketAsyncEventArgs 但事情也变得越来越好 处理这个问题很复杂。

  2. 阻止与异步:因为 阻止调用这可能很困难 或者不可能。如果是这样,那很好,但是 你怎么解决它 异步情况?

  3. 退出令牌?:我已尝试向其他方面发送某种“退出”令牌,以便它知道关机,但还没有让它工作,我想要在这里提供一个最小的例子。

  4. 无论如何需要异常处理:我知道真实应用程序中需要进行异常处理,因为网络连接等将失败。但是,如果没有例外,是否可以处理预期的正常关闭的情况?

  5. 编辑:我移动了原始文件并将代码回复到gist

    原始失败示例:已移至此处:https://gist.github.com/958955#file_original.cs

    当前工作答案https://gist.github.com/958955#file_answer.cs

    class Channel
    {
        private readonly TcpClient client;
        private readonly NetworkStream stream;
        private readonly string name;
        private readonly ManualResetEvent quit = new ManualResetEvent(false);
    
        public Channel(string name, TcpClient client)
        {
            this.name = name;
            this.client = client;
            stream = client.GetStream();
        }
    
        public void Run()
        {
            Console.WriteLine(name + ": connected");
            byte[] buffer = new byte[client.Client.ReceiveBufferSize];
            stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer);
    
            var writer = new StreamWriter(stream, Encoding.ASCII);
    
            while (true)
            {
                var line = Console.ReadLine();
                if (String.IsNullOrEmpty(line) || !this.client.Connected) break;
    
                writer.WriteLine(line);
                writer.Flush();
            }
    
            if (client.Connected)
            {
                Console.WriteLine(name + " shutting down send.");
                client.Client.Shutdown(SocketShutdown.Send);
                Console.WriteLine(name + " waiting for read to quit.");
                quit.WaitOne();
            }
            else
            {
                Console.WriteLine(name + " socket already closed");
            }
    
            Console.WriteLine(name + " quit, press key to exit.");
            Console.ReadKey();
        }
    
        private void Read(IAsyncResult result)
        {
            var bytesRead = this.stream.EndRead(result);
            if (bytesRead == 0)
            {
                Console.WriteLine(name + " read stopped, closing socket.");
                this.client.Close();
                this.quit.Set();
                return;
            }
    
            var buffer = (byte[])result.AsyncState;
            Console.WriteLine(name + " recieved:" + Encoding.ASCII.GetString((byte[])result.AsyncState, 0, bytesRead));
    
            stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer);
        }
    }
    

3 个答案:

答案 0 :(得分:2)

在调用Socket.Close()之前使用Socket.Shutdown()。等待关闭处理完成(例如,ReceiveAsync()将返回0字节)。

  1. 抽象选择(大多数情况下)不是问题。
  2. 异步I / O意味着永远不必看到对不起。
  3. 来自Socket.ReceiveAsync()方法的MS文档:对于字节流,已读取的零字节表示正常关闭,并且不会再读取更多字节。

答案 1 :(得分:0)

TcpClient实现了IDisposable,所以你应该能够像这样使用它然后不用担心关闭客户端 - using语句应该为你做,无论是否抛出异常:

class Client
{
    static void Main(string[] args)
    {
        using (TcpClient client = new TcpClient(AddressFamily.InterNetwork))
        {

            Console.WriteLine("client: created, press key to connect");
            Console.ReadKey();

            client.Connect(IPAddress.Loopback, 5000);

            var channel = new Channel("client", client);
            channel.Run();
        }
    }
}

也就是说,通常实现IDisposable的CLR类型会明确表示他们会关闭文档中的特定底层资源(例如:SqlConnection),但问题的TcpClient docs是奇怪的安静 - 你可能想先测试一下。

答案 2 :(得分:0)

我暂时没有完成套接字,但我记得你必须为他们调用shutdown()。

我想,.NET等价物将是socket.Shutdown(SocketShutdown.Both)