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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:35