.NET Core 3.1中通过QUIT命令正确关闭C# Socket的方法及ObjectDisposedException异常解决
解决.NET Core 3.1中Socket通过QUIT命令优雅关闭的问题
这个异常的核心原因很明确:你在主线程中关闭了Socket对象,但后台的接收线程(clientReceiver/serverReciever)还在无限循环里尝试调用Receive()方法,此时Socket已经被释放,自然会抛出ObjectDisposedException。你之前添加的return只让主线程退出,并没有通知接收线程停止运行。
下面是针对客户端和服务端的具体修改方案,确保通过QUIT命令优雅关闭连接且不触发异常:
一、通用核心思路
- 引入线程安全的停止标志:用
volatile bool类型变量控制接收线程的循环,确保多线程间的可见性。 - 先通知线程停止,再关闭Socket:避免接收线程在Socket释放后仍尝试访问它。
- 捕获Socket相关异常:处理连接断开时的预期异常,让线程优雅退出。
- 正确的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
相关产品推荐
相关产品推荐

