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

.NET Core 3.1中通过QUIT命令正确关闭C# Socket的方法及ObjectDisposedException异常解决

解决.NET Core 3.1中Socket通过QUIT命令优雅关闭的问题

这个异常的核心原因很明确:你在主线程中关闭了Socket对象,但后台的接收线程(clientReceiver/serverReciever)还在无限循环里尝试调用Receive()方法,此时Socket已经被释放,自然会抛出ObjectDisposedException。你之前添加的return只让主线程退出,并没有通知接收线程停止运行。

下面是针对客户端和服务端的具体修改方案,确保通过QUIT命令优雅关闭连接且不触发异常:

一、通用核心思路

  1. 引入线程安全的停止标志:用volatile bool类型变量控制接收线程的循环,确保多线程间的可见性。
  2. 先通知线程停止,再关闭Socket:避免接收线程在Socket释放后仍尝试访问它。
  3. 捕获Socket相关异常:处理连接断开时的预期异常,让线程优雅退出。
  4. 正确的Socket关闭顺序:先调用Shutdown(SocketShutdown.Both)确保所有数据收发完成,再调用Close()释放资源。

二、客户端代码修改(Client.cs)

1. 添加线程安全停止标志

在Client类中添加静态变量:

static volatile bool shouldStop = false;

2. 修改接收线程逻辑

更新clientReceiver方法,用停止标志控制循环,并添加异常捕获:

static void clientReceiver() {
    while (!shouldStop) {
        Thread.Sleep(500);
        try {
            byte[] buffer = new byte[300];
            int rece = serverSocket.Receive(buffer, 0, buffer.Length, 0);
            
            // 若接收长度为0,说明服务端已正常关闭连接
            if (rece == 0) {
                Console.WriteLine("Server closed the connection");
                shouldStop = true;
                break;
            }

            Array.Resize(ref buffer, rece);
            string receivedMsg = Encoding.Default.GetString(buffer);

            if (!connected) {
                Console.WriteLine("[" + DateTime.Now.ToString() + "] " + receivedMsg + " Connected!");
                connected = true;
            } else {
                if (receivedMsg == "QUIT") {
                    Console.WriteLine("Server Shutdown");
                    shouldStop = true;
                    serverSocket.Shutdown(SocketShutdown.Both);
                    serverSocket.Close();
                } else {
                    Console.WriteLine("[" + DateTime.Now.ToString() + "] " + receivedMsg);
                }
            }
        } catch (SocketException ex) {
            // 捕获连接异常,避免崩溃
            if (!shouldStop) {
                Console.WriteLine("Connection error: " + ex.Message);
                shouldStop = true;
            }
        } catch (ObjectDisposedException) {
            // Socket已释放,直接退出循环
            shouldStop = true;
        }
    }
}

3. 修改主线程输入处理逻辑

更新StartClient中的循环,用停止标志替代serverSocket.Connected(Connected属性无法实时反映连接状态):

printSession();
while (!shouldStop) {
    string input = Console.ReadLine();
    if (input == "QUIT") {
        shouldStop = true;
        byte[] sdata = Encoding.Default.GetBytes(input);
        serverSocket.Send(sdata, sdata.Length, 0);
        serverSocket.Shutdown(SocketShutdown.Both);
        serverSocket.Close();
        // 等待接收线程完全退出,避免资源泄漏
        processThread.Join();
        break;
    }
    byte[] sdata = Encoding.Default.GetBytes(input);
    serverSocket.Send(sdata, 0, sdata.Length, 0);
}

三、服务端代码修改(Server.cs)

服务端的问题和客户端完全一致,只需做类似修改:

1. 添加线程安全停止标志

在Server类中添加:

static volatile bool shouldStop = false;

2. 修改接收线程逻辑

更新serverReciever方法:

static void serverReciever() {
    while (!shouldStop) {
        Thread.Sleep(500);
        try {
            byte[] buffer = new byte[300];
            int rece = clientSocket.Receive(buffer, 0, buffer.Length, 0);
            
            if (rece == 0) {
                Console.WriteLine("Client closed the connection");
                shouldStop = true;
                break;
            }

            Array.Resize(ref buffer, rece);
            string receivedMsg = Encoding.Default.GetString(buffer);

            if (!connected) {
                Console.WriteLine("[" + DateTime.Now.ToString() + "] " + receivedMsg + " Connected!");
                connected = true;
            } else {
                if (receivedMsg == "QUIT") {
                    Console.WriteLine("Client disconnected from the chat");
                    shouldStop = true;
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                } else {
                    Console.WriteLine("[" + DateTime.Now.ToString() + "] " + receivedMsg);
                }
            }
        } catch (SocketException ex) {
            if (!shouldStop) {
                Console.WriteLine("Connection error: " + ex.Message);
                shouldStop = true;
            }
        } catch (ObjectDisposedException) {
            shouldStop = true;
        }
    }
}

3. 修改主线程循环逻辑

更新StartServer中的发送循环:

printSession();
processThread.Start();
byte[] name = Encoding.Default.GetBytes(username);
clientSocket.Send(name);

while (!shouldStop) {
    string input = Console.ReadLine();
    if (input == "QUIT") {
        shouldStop = true;
        byte[] sdata = Encoding.Default.GetBytes(input);
        clientSocket.Send(sdata, 0, sdata.Length, 0);
        clientSocket.Shutdown(SocketShutdown.Both);
        clientSocket.Close();
        processThread.Join();
        break;
    }
    byte[] sdata = Encoding.Default.GetBytes(input);
    clientSocket.Send(sdata, 0, sdata.Length, 0);
}

四、关键说明

  • volatile关键字:确保shouldStop变量在多线程间的可见性,避免线程缓存旧值导致循环无法退出。
  • 接收长度判断:当Receive()返回0时,说明对方已正常关闭连接,此时可以安全停止线程。
  • Thread.Join():等待接收线程完全退出后再结束主线程,确保所有Socket资源被正确释放。

内容的提问来源于stack exchange,提问作者Ammar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 05:47:29