简体   繁体   English

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

[英]Replacing Socket.ReceiveAsync with NetworkStream.ReadAsync (awaitable)

I have an application that makes a couple hundred TCP connections at the same time, and receives a constant stream of data from them. 我有一个应用程序,它同时生成几百个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); }
    }

My attempts led to something like this: 我的尝试导致了这样的事情:

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);
        }
    }

The issue I had was the method calling StartReceive() would block, and not get to the accompanying StartSend() method called after StartReceive() . Creating a new task for 我遇到的问题是调用StartReceive()的方法会阻塞,而不是调用StartReceive()调用的伴随的StartSend() method called after . Creating a new task for . Creating a new task for StartReceive() would just end up with 300-ish threads, and it seems to do so just by calling StartReceive()` anyways. . Creating a new task for StartReceive() . Creating a new task for would just end up with 300-ish threads, and it seems to do so just by calling StartReceive()来实现。

What would be the correct method of implementing the new async and await keywords on my existing code while using a NetworkStream so it is using the thread pool that Socket.SendAsync() and Socket.ReceiveAsync() are using to avoid having to have hundreds of threads/tasks? 在使用NetworkStream ,在我现有代码上实现新asyncawait关键字的正确方法是什么,因此它使用Socket.SendAsync()Socket.ReceiveAsync()使用的线程池来避免必须拥有数百个线程/任务?

Is there any performance advantage of using networkstream in this manner over i/o completion ports with beginreceive ? 以这种方式使用networkstream比使用beginreceive i / o完成端口有任何性能优势?

You're changing two things at once here: the asynchronous style ( SocketAsyncEventArgs to Task / async ) and the level of abstraction ( Socket to NetworkStream ). 你在这里一次改变两件事:异步样式( SocketAsyncEventArgsTask / async )和抽象级别( SocketNetworkStream )。

Since you're already comfortable with Socket , I recommend just changing the asynchronous style, and continue using the Socket class directly. 由于您已经熟悉Socket ,我建议只更改异步样式,并继续直接使用Socket类。

The Async CTP doesn't give Socket any async -compatible methods (which is weird; I assume they were left out by mistake and will be added in .NET 4.5). Async CTP没有给Socket任何async兼容的方法(这很奇怪;我认为它们被错误地遗漏了,并将在.NET 4.5中添加)。

It's not that hard to create your own ReceiveAsyncTask extension method (and similar wrappers for other operations) if you use my AsyncEx library : 如果您使用我的AsyncEx库,那么创建自己的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);
}

Once you do that, your StartReceive can be written as such: 完成后,您的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
  }
}

Now, to address many minor points: 现在,要解决许多小问题:

  • await doesn't spawn a new thread. await不会产生新的线程。 I wrote up an async/await intro on my blog , as have many others. 我和其他许多人一样, 在我的博客上写了一篇async / await简介 async / await allows concurrency, but that doesn't necessarily imply multithreading. async / await允许并发,但这并不一定意味着多线程。
  • Hundreds of threads can be problematic. 数以百计的线程可能会有问题。 Hundreds of tasks , though, are not a problem at all; 但是,数以百计的任务根本不是问题; the thread pool and BCL are designed to handle many, many tasks. 线程池和BCL旨在处理许多任务。
  • async / await is not a brand new form of asynchronous processing; async / await不是一种全新的异步处理形式; it's just an easier way to express asynchronous processing. 它只是一种表达异步处理的简单方法 It still uses IOCPs underneath. 它仍然使用下面的IOCP。 async / await has slightly lower performance than the lower-level methods; async / await性能略低于低级方法; its appeal is the ease of writing and composing asynchronous methods. 它的吸引力在于编写和编写异步方法的简易性。
  • A very busy system can see some increased GC pressure when it switches to async / await . 一个非常繁忙的系统在切换到async / await时会看到一些GC压力增加。 Stephen Toub on the Parallel Team wrote up some example socket-specific awaitables that can help with that issue. 并行团队的Stephen Toub 写了一些示例套接字特定的等待 ,可以帮助解决这个问题。 (I recommend using the straightforward pattern first, and only using the performance-enhanced approach if you find it necessary; still, it's good to know it's out there if you do end up needing it). (我建议首先使用简单的模式,如果你认为有必要,只使用性能增强的方法;但是,如果最终需要它,最好知道它在那里)。
  • Async methods should return Task unless you really need them to return void . 异步方法应返回Task除非您确实需要它们返回void Task is awaitable, so your method is composable (and more easily testable); Task是可以接受的,因此您的方法是可组合的(并且更容易测试); void is more like "fire and forget". void更像是“火与忘”。
  • You can call ConfigureAwait(false) to tell the rest of the async method to execute on a thread pool thread. 您可以调用ConfigureAwait(false)来告诉其余的async方法在线程池线程上执行。 I use this in my example above so that ProcessData is executed in a thread pool thread, just like it was when using SocketAsyncEventArgs . 我在上面的例子中使用它,以便ProcessData在线程池线程中执行,就像使用SocketAsyncEventArgs
  • Socket.Connected is useless. Socket.Connected没用。 You need to send data to detect if the connection is still valid. 您需要发送数据以检测连接是否仍然有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM