使用TcpListener进行异步/等待或开始/结束?

Dav*_*ave 6 .net c# tcplistener async-await c#-5.0

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

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

问题是,当我开始编写使用异步服务器/ AWAIT,我想通了,在TCP多用户服务器的使用情况,异步/ 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

注1来自

var acceptedClient = await _tcpListener.AcceptTcpClientAsync();
Run Code Online (Sandbox Code Playgroud)

var acceptedClient = _tcpListener.AcceptTcpClient();
Run Code Online (Sandbox Code Playgroud)

并注2

int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, 1024);
Run Code Online (Sandbox Code Playgroud)

int packetSize = acceptedClient.GetStream().Read(buffer, 0, 1024);
Run Code Online (Sandbox Code Playgroud)

服务器的工作方式完全相同.

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

Should I keep using IOCP in that case? because for me it's pretty easy and comfortable but I am afraid that it will be obsoleted or even no more available in newer .NET versions.

svi*_*ick 17

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

我认为你需要正确的术语.

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

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

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

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


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

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