【问题标题】:Accept TCP Client Async接受 TCP 客户端异步
【发布时间】:2014-04-07 02:02:21
【问题描述】:

我一直在做服务器。我在 async 方法中使用 TcpListener.AcceptTcpClientAsync() ,但我不知道如何实际使其工作。我现在的代码是:

private static async void StartServer()
{
    Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
    var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
    listener.Start();
    var client = await listener.AcceptTcpClientAsync();
}

如何处理客户?我是继续编码,它会自动创建相同方法的新线程,还是我需要做一些神奇的方法来为我做这件事?

编辑:当前代码:

private static Task HandleClientAsync(TcpClient client)
{
    var stream = client.GetStream();
    // do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
    while (true)
    {
        Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
        var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
        listener.Start();
        var client = await listener.AcceptTcpClientAsync();
        await HandleClientAsync(client);
    }
}

【问题讨论】:

标签: c# tcp tcpclient tcplistener


【解决方案1】:

// 简而言之,所有功劳都应归功于 c# 7.0(Joseph Albahari 和 Ben Albahari)

    async void RunServerAsync()
    {
        var listner = new TcpListener(IPAddress.Any, 9999);
        listner.Start();
        try
        {
            while (true)
                await Accept(await listner.AcceptTcpClientAsync());
        }
        finally { listner.Stop(); }
    }


    const int packet_length = 2;  // user defined packet length

    async Task Accept(TcpClient client)
    {
        await Task.Yield();
        try
        {
            using(client)
            using(NetworkStream n = client.GetStream())
            {
                byte[] data = new byte[packet_length];
                int bytesRead = 0;
                int chunkSize = 1;

                while (bytesRead < data.Length && chunkSize > 0)
                    bytesRead += chunkSize = 
                        await n.ReadAsync(data, bytesRead, data.Length - bytesRead);

                // get data
                string str = Encoding.Default.GetString(data);
                Console.WriteLine("[server] received : {0}", str);

                // To do
                // ...

                // send the result to client
                string send_str = "server_send_test";
                byte[] send_data = Encoding.ASCII.GetBytes(send_str);
                await n.WriteAsync(send_data, 0, send_data.Length);

            }
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

【讨论】:

  • 为什么是await Task.Yield();?
【解决方案2】:

没有什么会神奇地为您创建专用线程,尽管有一些线程用于 IO 完成,可以发挥作用,尤其是在您没有同步的情况下您需要返回的上下文。

您应该决定是希望您的 StartServer 方法在接受单个连接时真正完成,还是继续循环直到您被告知关闭。

无论哪种方式,您显然也需要决定如何处理客户。您可以启动一个新线程并使用同步方法,或者您可以只使用异步 IO 来处理同一线程中的所有内容。例如,要将传入的数据转储到文件中:

private Task HandleClientAsync(TcpClient client)
{
    // Note: this uses a *synchronous* call to create the file; not ideal.
    using (var output = File.Create("client.data"))
    {
        using (var input = client.GetStream())
        {
            // Could use CopyToAsync... this is just demo code really.

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                await output.WriteAsync(buffer, 0, bytesRead);
            }
        }
    }
}

(假设客户端在完成写入数据后将终止连接。)除了File.Create 调用之外,这都是异步的 - 因此无需为其创建单独的线程。

当然,这只是一个例子——真正的连接处理通常会更复杂。如果您的实际处理需要更多计算密集型,您可能需要考虑使用Task.Run 来使用线程池...这样就不会干扰接受更多连接。

【讨论】:

  • 我希望服务器继续循环。它将在while 循环中,获取传入的字符串,对其进行处理,然后返回另一个字符串或文件。
  • @Ilan321: 对 - 所以你需要将 while 循环添加到你的 StartServer 方法中(我会改为 RunServerAsync,并让它返回一个 Task 以便无论何时调用它都可以看到它何时完成)。您可能还想查看我为“介绍异步”演讲编写的演示 HTTP RPC 处理程序:github.com/jskeet/DemoCode/blob/master/AsyncIntro/Code/…(不是生产代码,但它可能对您有用。)
  • 我要编辑我的问题并输入我当前的代码。它会起作用吗?
  • @Ilan321:你不想在循环中创建一个新的监听器——你只需要在循环中调用AcceptTcpClientAsync(以及开始处理它的调用)。而且您不想想要等待HandleClientAsync 的结果 - 否则您一次只能处理一个请求。您可能希望创建一个“正在进行的请求”(由任务表示)的集合,以便您可以知道一切何时完成。再次,查看我链接到的代码。
  • 会的!感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2010-10-15
  • 2016-05-24
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
相关资源
最近更新 更多