6

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

到目前为止,我将 IOCP 用于 tcp 服务器,这非常简单和舒适,但这次我想使用 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 版本中不再可用。

4

3 回答 3

17

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

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

拥有BeginOperationEndOperation方法称为异步编程模型(APM)。有一个Task(或Task<T>)返回方法称为基于任务的异步模式(TAP)。I/O 完成端口(IOCP) 是一种在 Windows 上处理异步操作的方法,同时使用APM 和 TAP 的异步 I/O 方法使用它们。

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

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

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


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

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

经过几次搜索,我发现了这个

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

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

于 2016-01-15T06:31:22.273 回答
0

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

异步等待 tcp 服务器

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

于 2017-11-22T06:23:32.310 回答