You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用EndReceive时抛出ObjectDisposedException的异步Socket客户端问题

问题分析与修复方案

嘿,我仔细看了你的代码,发现问题出在异步接收的循环没有正确终止上。当你调用theClient.Shutdown(SocketShutdown.Both);和theClient.Close();时,Socket上还挂着一个未完成的BeginReceive操作,这时候关闭Socket会直接中断这个异步接收,从而触发你看到的EndReceive行的异常。

为啥会出现这个异常?

你的ReceiveCallback里不管有没有收到完整响应,都会再次调用BeginReceive,相当于让Socket一直处于“等数据”的状态。哪怕你已经拿到了模块的响应,这个循环还在继续发起新的接收请求。当你关闭Socket时,这个还在等待的异步操作就会被强制打断,抛出SocketException(通常错误码是10054或者10004)。

另外你的接收逻辑还有个小隐患:receiveDone.Set()的时机太随意了,不管有没有收到完整数据都会触发,虽然你说功能正常,但如果模块响应分多次发送,可能会导致你提前结束等待,拿到不完整的响应。

具体怎么修?

我给你整理了几个关键修复点:

  1. 给接收加“结束判断”
    你得知道什么时候算收到了完整的响应——比如RN 171的响应是不是以特定字符(比如\r\n)结尾?或者有没有固定长度?找到这个结束标志后,在ReceiveCallback里判断,一旦收到完整响应,就不要再继续调用BeginReceive了。

  2. 标记停止接收的状态
    给StateObject加个布尔标志,用来告诉回调函数“不用再接收了”,避免在关闭Socket后还有新的接收请求发起。

  3. 捕获并处理正常关闭的异常
    就算做了前面的处理,偶尔还是可能出现异常,比如Socket关闭时刚好有挂起的接收操作,这时候我们可以捕获特定的Socket异常,判断是正常关闭导致的,就直接忽略。

修改后的代码示例

首先给StateObject加个停止接收的标志:

public class StateObject
{
    public Socket workSocket = null;
    public const int BufferSize = 256;
    public byte[] buffer = new byte[BufferSize];
    public StringBuilder sb = new StringBuilder();
    public bool StopReceiving = false; // 新增:标记是否停止接收
}

然后修改ReceiveCallback方法,加上结束判断和异常处理:

private static void ReceiveCallback(IAsyncResult ar)
{
    try
    {
        StateObject state = (StateObject)ar.AsyncState;
        Socket clientReceiveCB = state.workSocket;

        // 如果已经标记停止接收,直接退出
        if (state.StopReceiving)
        {
            return;
        }

        int bytesRead = clientReceiveCB.EndReceive(ar);
        if (bytesRead > 0)
        {
            string testResponse = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
            state.sb.Append(testResponse);

            // 这里替换成RN 171的响应结束判断逻辑
            // 示例:假设响应以"\r\n"结尾
            if (state.sb.ToString().EndsWith("\r\n"))
            {
                response = state.sb.ToString();
                receiveDone.Set();
                state.StopReceiving = true; // 标记停止,不再发起新接收
                return;
            }

            // 没收到完整响应,继续接收
            clientReceiveCB.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
        }
        else
        {
            // 对方主动关闭连接,处理剩余数据
            response = state.sb.ToString();
            receiveDone.Set();
            state.StopReceiving = true;
        }
    }
    catch (SocketException ex)
    {
        // 正常关闭Socket导致的异常,直接忽略
        if (ex.ErrorCode == 10054 || ex.ErrorCode == 10004)
        {
            Console.WriteLine("Socket正常关闭,忽略此异常");
        }
        else
        {
            Console.WriteLine("接收异常:" + ex.ToString());
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

额外的优化建议

你的代码里response、connectDone这些都是静态变量,要是RabbitMQ同时触发多次Start调用,肯定会出现线程安全问题。建议把这些改成实例变量,每个Electricity实例拥有自己的事件和响应变量:

public class Electricity
{
    private const int port = 2000;
    // 改成实例成员,避免多线程冲突
    private ManualResetEvent connectDone = new ManualResetEvent(false);
    private ManualResetEvent sendDone = new ManualResetEvent(false);
    private ManualResetEvent receiveDone = new ManualResetEvent(false);
    private string response = string.Empty;

    // ... 其他代码不变,只是把静态的事件和response改成实例的
}

这样多个请求之间就不会互相干扰了,稳定性会好很多。

内容的提问来源于stack exchange,提问作者Raphaël Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:38:17