c# - 在异步函数中使用异步?

标签 c# sockets networking asynchronous tcp

我正在制作一个简单的服务器来监听客户端,该服务器将读取客户端请求、进行一些计算、将响应发送回客户端并尽快再次关闭(有点类似于 HTTP)。

每秒钟可能有很多连接,所以我希望它尽可能快速和高效。

到目前为止,我能想到的最好的方法如下所示:

private static ManualResetEvent gate = new ManualResetEvent(false);

static async void ListenToClient(TcpListener listener)
{
    Console.WriteLine("Waiting for connection");
    TcpClient client = await listener.AcceptTcpClientAsync();
    Console.WriteLine("Connection accepted & establised");
    gate.Set(); //Unblocks the mainthread

    Stream stream = client.GetStream();
    byte[] requestBuffer = new byte[1024];
    int size = await stream.ReadAsync(requestBuffer, 0, requestBuffer.Length);

    //PSEUDO CODE: Do some calculations

    byte[] responseBuffer = Encoding.ASCII.GetBytes("Ok");
    await stream.WriteAsync(responseBuffer, 0, responseBuffer.Length);

    stream.Close();
    client.Close();
}
static void Main(string[] args)
{
    TcpListener listener = new TcpListener(IPAddress.Any, 8888);
    listener.Start();
    while (true)
    {
        gate.Reset(); 
        ListenToClient(listener);
        gate.WaitOne(); //Blocks the main thread and waits until the gate.Set() is called
    }
}

注意:为了这个例子和简单性,我没有像 try-catch 那样进行任何错误处理,我知道这里的响应总是“Ok”

这里的代码只是简单地等待一个连接,当它到达await listener.AcceptTcpClientAsync()时,它会跳回到whileloop并等待直到建立连接并且gate.Set( ) 被调用以便它可以再次监听新连接。所以这将同时允许多个客户端(特别是如果计算可能需要很长时间)

但是我应该改用 stream.ReadAsync() 还是 stream.Read()?我很好奇它是否重要,因为我已经在一个不会阻塞主线程的异步函数中。

所以我最后的问题是:

  1. 这是完成此任务的最佳/正确方法吗(也使用 ManualResetEvent 类)?
  2. 在这种情况下,在读取和写入流时使用异步或非异步操作会有什么不同吗? (因为我没有阻塞主线程)
  3. 如果它滞后,发送/接收数据需要 1-2 秒,在异步和非异步操作之间进行选择是否仍然重要?

新改进的更新

由于回答,我已将我的代码更新为:

private static ManualResetEvent gate = new ManualResetEvent(false);

static async Task ListenToClient(TcpListener listener)
{
    //Same Code
}
static void Main(string[] args)
{
    TcpListener listener = new TcpListener(IPAddress.Any, 8888);
    listener.Start();
    while (true)
    {
        gate.Reset();
        Task task = ListenToClient(listener);
        task.ContinueWith((Task paramTask) =>
            {
                //Inspect the paramTask
            });
        gate.WaitOne(); //Blocks the main thread and waits until the gate.Set() is called
    }
}

最佳答案

我立即看到两个常见的 async 错误:

async void

不要这样做。编译器甚至支持 async void 的唯一原因是为了处理现有的事件驱动接口(interface)。这不是其中之一,所以这是一个反模式。 async void 实际上会导致失去任何响应该任务或对其执行任何操作的方式,例如处理错误。

说到响应任务...

ListenToClient(listener);

您正在生成一个任务,但从未检查它的状态。如果该任务中出现异常,您会怎么做?无论如何它都没有被捕获,它只会被默默地忽略。至少您应该在任务完成后为其提供顶级回调。甚至像这样简单的事情:

ListenToClient(listener).ContinueWith(t =>
{
    // t is the task.  Examine it for errors, cancelations, etc.
    // Respond to error conditions here.
});

关于c# - 在异步函数中使用异步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24436021/

相关文章:

c++ - 使用 ESP8266 自动检测本地服务器

java - 组播发送性能

c# - 在 C# 中使用相等运算符

c# - OrderBy 具有来自另一个表的值

C读取调用缓冲额外字符

java - BufferedReader.read 在读取整个消息之前返回 -1

c# - .Net 成员(member)提供者和名字和姓氏

c# - 为什么我的随机数生成器在 C# 中看起来不是随机的?

python - 在 Python 套接字中接收自定义 Scapy 数据包

ios - 退出 View Controller 后不要调用回调 block