控制台应用中如何保持客户端Socket连接开放以收发多条消息
问题解决方案
核心问题根因
- 原有示例为单次请求设计,执行完单轮收发后无阻塞逻辑,主流程直接结束导致程序退出
- 接收回调逻辑冗余重复,
ManualResetEvent接收完成信号未做重置,无法支持多轮接收 - 无请求队列和响应匹配逻辑,无法应对请求堆叠、多请求响应对应的需求
具体修改步骤
1. 新增全局变量适配长连接场景
在AsynchronousClient类中新增线程安全的队列、计数和长连接Socket变量:
// 待发送请求队列,多线程场景下安全 private static ConcurrentQueue<string> _requestQueue = new ConcurrentQueue<string>(); // 当前请求剩余未接收的响应条数,每个请求对应2条 private static int _remainingResponseCount = 0; // 保持长连接的全局Socket实例 private static Socket _connectedClient;
2. 修改主流程为循环监听模式
修改StartClient方法,连接成功后持续保持接收状态,主进程循环监听控制台输入:
public static void StartClient() { try { IPEndPoint remoteEP = new IPEndPoint(ip, port); _connectedClient = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // 建立和服务器的连接 _connectedClient.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), _connectedClient); connectDone.WaitOne(); // 启动全局持续接收逻辑 Receive(_connectedClient); Console.WriteLine("连接成功,输入内容回车即可发送,输入exit可退出程序:"); // 主循环监听控制台输入,阻塞主进程避免程序退出 while(true) { var input = Console.ReadLine(); if(input.Equals("exit", StringComparison.OrdinalIgnoreCase)) { // 正常关闭连接后退出 _connectedClient.Shutdown(SocketShutdown.Both); _connectedClient.Close(); break; } if(!string.IsNullOrEmpty(input)) { _requestQueue.Enqueue(input); Send(_connectedClient, input + "\r\n"); sendDone.WaitOne(); sendDone.Reset(); // 重置发送完成信号,支持后续多次发送 } } } catch (Exception e) { Console.WriteLine(e.ToString()); } }
3. 修正接收回调逻辑
删除冗余的BeginReceive调用,新增响应分割、条数计数逻辑,适配单请求2条响应的需求:
private static void ReceiveCallback(IAsyncResult ar) { try { StateObject state = (StateObject)ar.AsyncState; Socket client = state.workSocket; int bytesRead = client.EndReceive(ar); if (bytesRead > 0) { state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); var receivedContent = state.sb.ToString(); // 按服务器响应分隔符拆分单条响应,默认按\r\n分割,可根据实际协议调整 while(receivedContent.Contains("\r\n")) { var lineEndIndex = receivedContent.IndexOf("\r\n"); var singleResponse = receivedContent.Substring(0, lineEndIndex); receivedContent = receivedContent.Substring(lineEndIndex + 2); Console.WriteLine($"收到响应:{singleResponse}"); _remainingResponseCount--; // 当前请求的2条响应全部接收完成 if(_remainingResponseCount == 0 && _requestQueue.TryDequeue(out _)) { // 队列中还有待处理请求的话,重置响应计数 _remainingResponseCount = 2; } } // 缓存未处理完的半条响应 state.sb.Clear(); state.sb.Append(receivedContent); // 继续挂起接收等待下一条数据 client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); } else { // 服务器主动断开连接 if (state.sb.Length > 1) { Console.WriteLine($"服务器断开前最后返回内容:{state.sb.ToString()}"); } client.Close(); } } catch (Exception e) { Console.WriteLine(e.ToString()); } }
4. 适配发送回调逻辑
发送成功后初始化对应响应计数:
private static void SendCallback(IAsyncResult ar) { try { Socket client = (Socket)ar.AsyncState; int bytesSent = client.EndSend(ar); Console.WriteLine("已发送 {0} 字节到服务器", bytesSent); // 如果当前没有等待的响应,设置当前请求需要接收2条响应 if(_remainingResponseCount == 0) { _remainingResponseCount = 2; } sendDone.Set(); } catch (Exception e) { Console.WriteLine(e.ToString()); } }
注意:如果你的服务器响应不是以
\r\n作为单条结束标识,需要根据实际的通信协议调整响应分割规则,保证能正确拆分出每一条独立响应。
内容的提问来源于stack exchange,提问作者Blaze
相关产品推荐
相关产品推荐

