简体   繁体   English

C# - 使用 TcpListener(异步)的最佳方式是什么

[英]C# - What's the best way to use TcpListener (async)

I would create a tcp server with TcpListener, but I don't know what's the best solution to do that.我会用 TcpListener 创建一个 tcp 服务器,但我不知道什么是最好的解决方案。 I tried with 3 examples.我尝试了 3 个示例。 See below.见下文。

Example 1 (I used BeginAcceptTcpClient)示例 1 (我使用了 BeginAcceptTcpClient)

class Program
  {
    static void Main(string[] args)
    {
      var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4567);
      var listener = new TcpListener(endPoint);

      listener.Start();

      AcceptTcpClient(listener);
      while (true)
      {
      }
    }

    public static void AcceptTcpClient(TcpListener listener)
    {
      listener.BeginAcceptTcpClient(ClientConnected, listener);
    }

    public static void ClientConnected(IAsyncResult asyncResult)
    {
      var listener = (TcpListener)asyncResult.AsyncState;
      var client = listener.EndAcceptTcpClient(asyncResult);
      AcceptTcpClient(listener);

      DoAsync(client);
    }
  }

Example 2 (I used BeginAcceptTcpClient with AutoResetEvent)示例 2 (我将 BeginAcceptTcpClient 与 AutoResetEvent 一起使用)

class Program1
  {
    private static readonly AutoResetEvent CONNECTION_WAIT_HANDLE = new AutoResetEvent(false);
    static void Main(string[] args)
    {
      var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4567);
      var listener = new TcpListener(endPoint);

      listener.Start();

      while (true)
      {
        listener.BeginAcceptTcpClient(ClientConnectedHandle, listener);
        CONNECTION_WAIT_HANDLE.WaitOne();
        CONNECTION_WAIT_HANDLE.Reset();
      }
    }

    public static void ClientConnectedHandle(IAsyncResult asyncResult)
    {
      var listener = (TcpListener)asyncResult.AsyncState;
      var client = listener.EndAcceptTcpClient(asyncResult);
      CONNECTION_WAIT_HANDLE.Set();

      DoAsync(client);
    }
  }

Example 3 (I used AcceptTcpClientAsync)示例 3 (我使用了 AcceptTcpClientAsync)

class Program2
  {
    static async Task Main(string[] args)
    {
      var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4567);
      var listener = new TcpListener(endPoint);

      listener.Start();

      while (true)
      {
        var client = await listener.AcceptTcpClientAsync();
        DoAsync(client);
      }
    }

    public static void AcceptTcpClient(TcpListener listener)
    {
      listener.BeginAcceptTcpClient(ClientConnected, listener);
    }

    public static void ClientConnected(IAsyncResult asyncResult)
    {
      var listener = (TcpListener)asyncResult.AsyncState;
      var client = listener.EndAcceptTcpClient(asyncResult);
      AcceptTcpClient(listener);

      DoAsync(client);
    }
  }

I think the best solution is the last (Example 3) but I'm not sure.我认为最好的解决方案是最后一个(示例 3),但我不确定。 What do you think of that?你对那个怎么想的?

Example 3 uses the task-based asynchronous pattern (TAP) which is the recommended asynchronous design pattern for new development as stated in the docs . 示例3使用基于任务的异步模式(TAP),这是docs中所述的针对新开发的推荐异步设计模式。

TAP uses a single method to represent the initiation and completion of an asynchronous operation. TAP使用一种方法来表示异步操作的启动和完成。 This contrasts with both the Asynchronous Programming Model (APM or IAsyncResult) pattern and the Event-based Asynchronous Pattern (EAP). 这与异步编程模型(APM或IAsyncResult)模式和基于事件的异步模式(EAP)形成对比。 APM requires Begin and End methods. APM需要Begin和End方法。 EAP requires a method that has the Async suffix and also requires one or more events, event handler delegate types, and EventArg-derived types. EAP需要一种具有Async后缀的方法,并且还需要一个或多个事件,事件处理程序委托类型和EventArg派生的类型。 Asynchronous methods in TAP include the Async suffix after the operation name for methods that return awaitable types, such as Task, Task, ValueTask, and ValueTask. TAP中的异步方法在操作名称之后包含Async后缀,用于返回可等待类型(例如Task,Task,ValueTask和ValueTask)的方法。

This is the code that I'm using in my project.这是我在项目中使用的代码。 It's a receive only asynchronous server but you can modify it to your liking according to your needs in Task.Run() .它是一个仅接收异步服务器,但您可以根据自己的需要在Task.Run()根据自己的喜好修改它。 I have commented out the code so that you can understand how it works.我已经注释掉了代码,以便您可以了解它是如何工作的。

static async Task Main(string[] args)
{
    await RunServer();
}

static async Task RunServer()
{
    TcpListener Listener = new TcpListener(IPAddress.Any, YOURPORTHERE); // Set your listener
    Listener.Start(); // Start your listener

    while (true) // Permanent loop, it may not be the best solution
    {
        TcpClient Client = await Listener.AcceptTcpClientAsync(); // Waiting for a connection
        _ = Task.Run(() => { // Connection opened. Queues the specified job to run in the ThreadPool, meanwhile the server is ready to accept other connections in parallel
            try
            {
                var Stream = Client.GetStream(); // (read-only) get data bytes
                if (Stream.CanRead) // Verify if the stream can be read.
                {
                    byte[] Buffer = new byte[Client.ReceiveBufferSize]; // Initialize a new empty byte array with the data length.
                    StringBuilder SB = new StringBuilder();
                    do // Start converting bytes to string
                    {
                        int BytesReaded = Stream.Read(Buffer, 0, Buffer.Length);
                        SB.AppendFormat("{0}", Encoding.ASCII.GetString(Buffer, 0, BytesReaded));
                    } while (Stream.DataAvailable); // Until stream data is available

                    if (SB != null) // Stream data is ready and converted to string
                        // Do some stuffs
                }
            }
            catch (Exception Ex) // In case of errors catch it to avoid the app crash
            {
                ConsoleMessage.Error(Ex.ToString()); // Detailed exception
            }
        });
    }
}

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

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