【问题标题】:How to avoid ObjectDisposed exception after closing TcpClient关闭 TcpClient 后如何避免 ObjectDisposed 异常
【发布时间】:2019-12-08 15:38:21
【问题描述】:

我对@9​​87654323@ 有一个待处理的ReadAsync 操作。

我的应用程序在确定不再可能发生通信时关闭TcpClient。在套接字上调用TcpClient.Close 会导致Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll 被先前对ReadAsync() 的调用抛出。

如何避免这种异常?

最小示例:(使用 putty、nc 等打开连接进行测试)

using System;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private TcpClient client;

        // Initialize the form
        public Form1()
        {
            InitializeComponent();
        }

        // Start the Listen() task when the form loads
        private void Form1_Load(object sender, EventArgs e)
        {
            var task = Listen();
        }

        // Listen for the connection
        public async Task Listen()
        {
            // Start the listener
            var listener = new TcpListener(IPAddress.Any, 80);
            listener.Start();

            // Accept a connection
            client = await listener.AcceptTcpClientAsync();

            // Wait for some data (the client doesn't send any for the sake of reproducing the issue)
            // An exception is generated in 'ReadAsync' after Button1 is clicked
            byte[] buffer = new byte[100];
            await client.GetStream().ReadAsync(buffer, 0, 100);
        }

        // I will click this button before the above call to `ReadAsync` receives any data
        private void button1_Click_1(object sender, EventArgs e)
        {
            // This causes an exception in the above call to `ReadAsync`
            client.Close();
        }
    }
}

CancellationToken can't be used 取消ReadAsync()。有没有办法在不引发异常的情况下关闭与待处理的ReadAsync() 的连接?除非绝对必要,否则我不想在这里使用try/catch

【问题讨论】:

    标签: c# winforms async-await network-programming


    【解决方案1】:

    调用Socket.Shutdown() 允许连接正常关闭,导致ReadAsync() 根据需要返回0

    然后可以在ReadAsync() 返回0 后调用TcpClient.Close() 来处理套接字对象。

    // Listen for the connection
    public async Task Listen()
    {
        // ...
    
        // Wait for some data (the client won't be sending any at this point)
        byte[] msgBuf = new byte[100];
        var lengthRead = await client.GetStream().ReadAsync(msgBuf, 0, 100);
        if (lengthRead == 0)
        {
            // A graceful shutdown has occurred. The socket can now be disposed
            // by "TcpClient.Close()"
            client.Close();
        }
    }
    
    // I will click this button to close the connection before the above call to `ReadAsync` receives any data
    private void button1_Click_1(object sender, EventArgs e)
    {
        // This causes ReadAsync() to return 0 after which the socket can be disposed
        client.Client.Shutdown(SocketShutdown.Both);
    }
    

    【讨论】:

      【解决方案2】:

      我已经实现了一个使用 TCPClient 侦听 TCP 端口的应用程序,而不是使用 Task Listen() 函数,为什么不使用 while 循环来保持连接打开,然后您可以在 while 循环中使用一些条件来关闭满足您的要求时的连接...

      【讨论】:

      • 我选择了 async/await 模式以避免不得不在单独的非 ui 线程上运行阻塞代码并不得不处理多线程的复杂性。
      猜你喜欢
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 2017-02-10
      • 1970-01-01
      • 2018-12-21
      • 2010-09-29
      • 2016-09-15
      相关资源
      最近更新 更多