简体   繁体   中英

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. I tried with 3 examples. See below.

Example 1 (I used 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)

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)

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. 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 .

TAP uses a single method to represent the initiation and completion of an asynchronous operation. This contrasts with both the Asynchronous Programming Model (APM or IAsyncResult) pattern and the Event-based Asynchronous Pattern (EAP). APM requires Begin and End methods. EAP requires a method that has the Async suffix and also requires one or more events, event handler delegate types, and EventArg-derived types. 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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