【问题标题】:async / await or Begin / End with TcpListener?async / await 还是 Begin / End 与 TcpListener?
【发布时间】:2013-10-11 19:04:31
【问题描述】:

我已经开始构建一个 tcp 服务器,它将能够接受许多客户端,并同时从所有客户端接收新数据。

直到现在,我都在 tcp 服务器上使用 IOCP,这非常简单和舒适, 但这次我想使用 Async / Await 技术。在 C# 5.0 中发布。

问题是,当我开始使用 async/await 编写服务器时,我发现在 tcp 多用户服务器用例中,async/await 技术。并且常规的同步方法也一样。

这是一个更具体的简单示例:

class Server
{
    private TcpListener _tcpListener;
    private List<TcpClient> _clients;
    private bool IsStarted;

    public Server(int port)
    {
        _tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, port));
        _clients = new List<TcpClient>();
        IsStarted = false;
    }

    public void Start()
    {
        IsStarted = true;
        _tcpListener.Start();
        Task.Run(() => StartAcceptClientsAsync());
    }

    public void Stop()
    {
        IsStarted = false;
        _tcpListener.Stop();
    }

    private async Task StartAcceptClientsAsync()
    {
        while (IsStarted)
        {
            // ******** Note 1 ********
            var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

            _clients.Add(acceptedClient);
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            Console.WriteLine("Accepted new client! IP: {0} Port: {1}", ipEndPoint.Address, ipEndPoint.Port);

            Task.Run(() => StartReadingDataFromClient(acceptedClient));
        }
    }

    private async void StartReadingDataFromClient(TcpClient acceptedClient)
    {
        try
        {
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            while (true)
            {
                MemoryStream bufferStream = new MemoryStream();
                // ******** Note 2 ********
                byte[] buffer = new byte[1024];
                int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, buffer.Length);

                if (packetSize == 0)
                {
                    break;
                }

                Console.WriteLine("Accepted new message from: IP: {0} Port: {1}\nMessage: {2}",
                    ipEndPoint.Address, ipEndPoint.Port, Encoding.Default.GetString(buffer));
            }
        }
        catch (Exception)
        { 
        }
        finally
        {
            acceptedClient.Close();
            _clients.Remove(acceptedClient);
        }
    }
}

现在,如果您看到“注 1”和“注 2”下的行, 它可以很容易地更改为:

注释 1 来自

var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

var acceptedClient = _tcpListener.AcceptTcpClient();

以及来自

的注释2
int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, 1024);

int packetSize = acceptedClient.GetStream().Read(buffer, 0, 1024);

而且服务器的工作方式完全相同。

那么,为什么在多个用户的 tcp 监听器中使用 async / await 和使用常规同步方法一样呢?

在这种情况下我应该继续使用 IOCP 吗?因为对我来说它非常简单和舒适,但我担心它会被淘汰,甚至在较新的 .NET 版本中不再可用。

【问题讨论】:

  • await同步的。特别是它不会阻塞任何线程。
  • 首先,感谢您的回答。如果我在新线程(新任务)中打开接受客户端或从客户端接收数据的函数,我为什么要关心它不会阻塞任何线程?再次,就像在常规同步方法中一样。
  • @Elad 在您的用例中,没有任何区别。事实上,使用异步创建了一项不必要的任务。
  • 我听说将我的代码与 async / await 一起使用需要更少的 cpu 工作 - 对吗?如果是的话,将其与 IOCP 进行比较时是否也正确?
  • @Elad:不,APM和TAP的性能基本相同; TAP 的好处是您的代码(通常)更易于维护。出于可扩展性的原因和(在一般情况下)同步 API 可能导致死锁,您确实希望对套接字使用异步方法(APM/EAP/TAP)。

标签: c# .net async-await c#-5.0 tcplistener


【解决方案1】:

到目前为止,我将 IOCP 用于 tcp 服务器,这非常简单和舒适,但这次我想使用 Async / Await 技术。在 C# 5.0 中发布。

我认为您需要正确使用术语。

拥有BeginOperationEndOperation 方法称为Asynchronous Programming Model (APM)。有一个Task(或Task&lt;T&gt;)返回方法称为Task-based Asynchronous Pattern(TAP)。 I/O Completion Ports (IOCP) 是一种在 Windows 上处理异步操作的方法,并且使用 APM 和 TAP 的异步 I/O 方法都使用它们。

这意味着 APM 和 TAP 的性能将非常相似。两者最大的区别在于使用 TAP 和 async-await 的代码比使用 APM 和回调的代码可读性强得多。

因此,如果您想(或必须)异步编写代码,请尽可能使用 TAP 和 async-await。但是,如果您没有充分的理由这样做,请同步编写代码。

在服务器上,使用异步的一个很好的理由是可伸缩性:异步代码可以同时处理更多的请求,因为它倾向于使用更少的线程。如果您不关心可扩展性(例如因为您不会同时拥有很多用户),那么异步就没有多大意义。


此外,您的代码包含一些您应该避免的做法:

  • 不要使用async void 方法,没有很好的方法来判断它们何时完成并且它们有错误的异常处理。事件处理程序是个例外,但这主要适用于 GUI 应用程序。
  • 如果没有必要,不要使用Task.Run()。如果你想离开当前线程(通常是 UI 线程)或者你想并行执行同步代码,Task.Run() 会很有用。但是在服务器应用程序中使用它来启动异步操作并没有多大意义。
  • 不要忽略从方法返回的Tasks,除非你确定它们不会抛出异常。来自被忽略的Tasks 的异常不会做任何事情,这很容易掩盖错误。

【讨论】:

  • 如果我没听错的话,你是说在我的代码示例中 async / await 是不必要的,我应该决定哪个选项更适合我?:1. 创建一个像我一样的服务器在代码示例中没有 async / await 2. 使用 APM 创建服务器
  • @Elad 不,这不是我要说的。我是说如果你需要可伸缩性和同步代码,你应该使用async-await。 APM 对 TAP 几乎没有优势。
  • 所以我不明白应该如何使用 async await 编写服务器,因为现在在我看来使用 async / await 和使用常规同步方法是一回事(就像我在代码示例),并且异步等待并没有通过打开新线程和创建 while (true) 循环来节省所有的脏活。你可能有一个好的代码示例给我吗?
  • 我知道这已经很多年了,但也许其他人偶然发现你的评论@Dave 并认为相同:async/await 和 syncronous 可能看起来相似,但它们并不相同;当您 await 某事时,正在执行您的代码的线程关闭并执行其他操作,而等待的任务正在完成。当您同步调用方法时,线程除了等待调用完成外,什么也不做。这就是为什么使用 async 你不需要线程“在方法需要时间完成时继续确保事情完成” - 线程一直忙于“做其他事情”
【解决方案2】:

经过几次搜索,我找到了this

:这是一份 TCPServer 实践列表,但哪一个是每秒管理 5000 多个客户端的最佳实践?

A:如果您同时使用数据库“异步方法和迭代器”,我的假设是“编写异步方法”会做得更多

【讨论】:

    【解决方案3】:

    这里是使用 async/await 的示例代码。

    async await tcp server

    我认为使用 async/await 构建 tcp 服务器比使用 iocp 更容易。

    【讨论】:

      猜你喜欢
      • 2018-03-21
      • 1970-01-01
      • 2018-10-18
      • 2020-10-14
      • 2014-03-16
      • 2012-12-04
      相关资源
      最近更新 更多