用NetworkStream.ReadAsync替换Socket.ReceiveAsync(等待)

时间:2012-03-16 07:11:32

标签: c# sockets asynchronous networkstream async-await

我有一个应用程序可以同时生成几百个TCP连接,并从它们接收一个恒定的数据流。

 private void startReceive()
    {
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();
        e.Completed += receiveCompleted;
        e.SetBuffer(new byte[1024], 0, 1024);
        if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }  
    }

    void receiveCompleted(object sender, SocketAsyncEventArgs e)
    {
        ProcessData(e);

        if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
    }

我的尝试导致了这样的事情:

private async void StartReceive()
    {
        byte[] Buff = new byte[1024];
        int recv = 0;
        while (Socket.Connected)
        {
            recv = await NetworkStream.ReadAsync(Buff, 0, 1024);
            ProcessData(Buff,recv);
        }
    }

我遇到的问题是调用StartReceive()的方法会阻塞,而不是到达随附的StartSend() method called after StartReceive(). Creating a new task for StartReceive()would just end up with 300-ish threads, and it seems to do so just by calling StartReceive()`反正。

在使用async时,在现有代码上实施新awaitNetworkStream关键字的正确方法是什么?所以它使用Socket.SendAsync()的线程池和Socket.ReceiveAsync()正在使用以避免必须拥有数百个线程/任务?

以这种方式使用networkstream与使用beginreceive的i / o完成端口相比,是否有任何性能优势?

1 个答案:

答案 0 :(得分:25)

您在这里一次更改两件事:异步样式(SocketAsyncEventArgsTask / async)和抽象级别(Socket到{{1 }})。

由于您已熟悉NetworkStream,我建议您只更改异步样式,然后直接继续使用Socket类。

Async CTP没有给Socket任何Socket兼容的方法(这很奇怪;我认为它们被错误地遗漏了,并将在.NET 4.5中添加)。

如果您使用我的AsyncEx library,那么创建自己的async扩展方法(以及其他操作的类似包装器)并不难:

ReceiveAsyncTask

完成此操作后,您的public static Task<int> ReceiveAsyncTask(this Socket socket, byte[] buffer, int offset, int size) { return AsyncFactory<int>.FromApm(socket.BeginReceive, socket.EndReceive, buffer, offset, size, SocketFlags.None); } 可以这样写:

StartReceive

现在,要解决许多小问题:

  • private async Task StartReceive() { try { var buffer = new byte[1024]; while (true) { var bytesReceived = await socket.ReceiveAsyncTask(buffer, 0, 1024) .ConfigureAwait(false); ProcessData(buffer, bytesReceived); } } catch (Exception ex) { // Handle errors here } } 不会产生新线程。我和其他许多人一样写了an async/await intro on my blogawait / async允许并发,但这并不一定意味着多线程。
  • 数以百计的线程可能会有问题。但是,数百个任务根本不是问题;线程池和BCL旨在处理许多任务。
  • await / async不是一种全新的异步处理形式;它只是一种更简单的方式表达异步处理。它仍然使用下面的IOCP。 await / async的效果略低于较低级别的方法;它的吸引力在于编写和编写异步方法的易用性。
  • 非常繁忙的系统在切换到await / async时可能会看到GC压力增加。并联团队wrote up some example socket-specific awaitables上的Stephen Toub可以帮助解决这个问题。 (我建议首先使用简单的模式,如果你觉得有必要,只使用性能增强的方法;但是,如果最终需要它,最好知道它在那里。)
  • 异步方法应该返回await,除非你真的需要它们才能返回Taskvoid是等待的,因此您的方法是可组合的(并且更容易测试); Task更像是“火与忘记”。
  • 您可以调用void告诉其余的ConfigureAwait(false)方法在线程池线程上执行。我在上面的示例中使用了这个,以便在线程池线程中执行async,就像使用ProcessData时一样。
  • SocketAsyncEventArgs没用。 You need to send data to detect if the connection is still valid.