C#中如何异步监听Socket同时实现数据发送?
在持续监听Socket的同时实现数据回传的解决方案
我需要异步监听Socket,但同时希望能通过同一Socket回传数据,当前代码因处于持续循环中无法实现该操作。请问如何在持续监听的同时实现Socket数据回传?以下是我用于异步连接和监听的代码:
sock1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IAsyncResult result; result = sock1.BeginConnect(Host1, Comm.PortInt, null, null); bool success = result.AsyncWaitHandle.WaitOne(1000, true); if (!success) { sock1.Close(); throw new Exception("Could not connect to " + Host);//baseDissConnects } sock1.SendTimeout = 1000; sock1.ReceiveTimeout = 1000; //sock1.Connect(Host1, Comm.PortInt); sock1.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 512 * 1024); while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) //cool way to exit { if (IsConnected(sock1)) { if (sock1.Available > 0) { ProcessResponse(sock1); } } }不确定是否需要通过多线程实现,恳请提供帮助。
你的问题核心在于当前的轮询方式把主线程死死占住了,导致没法腾出时间处理数据回传操作。其实不需要额外开多线程(当然也可以,但异步API本身就能更优雅地解决),推荐用Socket的异步接收API来实现非阻塞监听,这样主线程就能自由处理发送请求了。
方案一:使用传统的异步BeginReceive/EndReceive
这种是经典的异步Socket模式,通过回调处理接收到的数据,完全不会阻塞主线程:
private Socket _sock1; private byte[] _receiveBuffer = new byte[512 * 1024]; public void StartSocket() { _sock1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IAsyncResult result = _sock1.BeginConnect(Host1, Comm.PortInt, OnConnectCompleted, null); bool success = result.AsyncWaitHandle.WaitOne(1000, true); if (!success) { _sock1.Close(); throw new Exception("Could not connect to " + Host); } _sock1.SendTimeout = 1000; _sock1.ReceiveTimeout = 1000; _sock1.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 512 * 1024); // 启动异步接收,立即返回,不阻塞主线程 StartAsyncReceive(); // 主线程在这里处理用户输入或发送逻辑 while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { // 示例:如果有需要发送的数据,直接调用Send即可 // if (hasDataToSend) // { // byte[] sendBuffer = Encoding.UTF8.GetBytes("Hello Server"); // _sock1.Send(sendBuffer); // } Thread.Sleep(10); // 避免空循环占用过高CPU } _sock1.Close(); } private void OnConnectCompleted(IAsyncResult ar) { try { _sock1.EndConnect(ar); Console.WriteLine("连接成功"); } catch (Exception ex) { Console.WriteLine("连接失败:" + ex.Message); } } private void StartAsyncReceive() { if (!_sock1.Connected) return; // 开始异步接收,数据到达时触发回调 _sock1.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, SocketFlags.None, OnReceiveCompleted, null); } private void OnReceiveCompleted(IAsyncResult ar) { try { int bytesRead = _sock1.EndReceive(ar); if (bytesRead > 0) { // 处理接收到的数据,这里可以直接调用回传逻辑 ProcessResponse(_sock1, _receiveBuffer, bytesRead); // 继续监听下一批数据 StartAsyncReceive(); } else { Console.WriteLine("远端关闭连接"); _sock1.Close(); } } catch (Exception ex) { Console.WriteLine("接收数据出错:" + ex.Message); _sock1.Close(); } } // 调整ProcessResponse方法以接收缓冲区数据 private void ProcessResponse(Socket sock, byte[] buffer, int bytesRead) { // 你的数据解析逻辑 // 直接在这里调用sock.Send(...)就能实现回传,Socket发送操作是线程安全的 }
方案二:使用现代Task-based异步API(推荐)
如果使用.NET Framework 4.5+或.NET Core/.NET 5+,推荐用ReceiveAsync这类基于Task的异步方法,代码更简洁,还能配合async/await语法:
private Socket _sock1; private byte[] _receiveBuffer = new byte[512 * 1024]; public async Task StartSocketAsync() { _sock1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 带超时的连接逻辑 var connectTask = _sock1.ConnectAsync(Host1, Comm.PortInt); if (await Task.WhenAny(connectTask, Task.Delay(1000)) != connectTask) { _sock1.Close(); throw new Exception("Could not connect to " + Host); } _sock1.SendTimeout = 1000; _sock1.ReceiveTimeout = 1000; _sock1.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 512 * 1024); // 启动异步接收循环,不阻塞主线程 _ = ReceiveLoopAsync(); // 主线程处理用户输入和发送逻辑 while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { // 示例:读取用户输入并发送 // string input = Console.ReadLine(); // if (!string.IsNullOrEmpty(input)) // { // byte[] sendBuffer = Encoding.UTF8.GetBytes(input); // await _sock1.SendAsync(sendBuffer, SocketFlags.None); // } await Task.Delay(10); // 降低CPU占用 } _sock1.Close(); } private async Task ReceiveLoopAsync() { try { while (_sock1.Connected) { int bytesRead = await _sock1.ReceiveAsync(_receiveBuffer, SocketFlags.None); if (bytesRead == 0) { Console.WriteLine("远端关闭连接"); break; } // 处理数据并可直接回传 ProcessResponse(_sock1, _receiveBuffer, bytesRead); } } catch (Exception ex) { Console.WriteLine("接收出错:" + ex.Message); } finally { _sock1.Close(); } }
关键注意点:
- 原代码的
while轮询Available会占用主线程所有资源,导致无法处理发送;异步接收是操作系统主动通知数据到达,主线程可以自由处理其他逻辑。 - Socket的
Send和Receive操作本身是线程安全的,所以在回调或异步任务中调用Send回传数据完全没问题。 - 不需要额外创建线程,异步API利用了系统IOCP机制,比手动开线程更高效。
内容的提问来源于stack exchange,提问作者azuric
相关产品推荐
相关产品推荐

