我对服务器/客户端架构有以下要求:

  • 编写一个异步工作的服务器/客户端。
  • 通信需要是双工的,即两端读写。
  • 多个客户端可以在任何给定时间连接到服务器。
  • 服务器/客户端应该等到它们可用并最终建立连接。
  • 一旦客户端连接,它应该写入流。
  • 然后服务器应该从流中读取并将响应写回客户端。
  • 最后,客户端应该读取响应并且通信应该结束。

  • 因此,考虑到以下要求,我编写了以下代码,但我不太确定,因为管道的文档有些缺乏,不幸的是,代码似乎无法正常工作,它在某个点挂起.
    namespace PipesAsyncAwait471
    {
        using System;
        using System.Collections.Generic;
        using System.IO.Pipes;
        using System.Linq;
        using System.Threading.Tasks;
    
        internal class Program
        {
            private static async Task Main()
            {
                List<Task> tasks = new List<Task> {
                    HandleRequestAsync(),
                };
    
                tasks.AddRange(Enumerable.Range(0, 10).Select(i => SendRequestAsync(i, 0, 5)));
    
                await Task.WhenAll(tasks);
            }
    
            private static async Task HandleRequestAsync()
            {
                using (NamedPipeServerStream server = new NamedPipeServerStream("MyPipe",
                                                                                PipeDirection.InOut,
                                                                                NamedPipeServerStream.MaxAllowedServerInstances,
                                                                                PipeTransmissionMode.Message,
                                                                                PipeOptions.Asynchronous))
                {
                    Console.WriteLine("Waiting...");
    
                    await server.WaitForConnectionAsync().ConfigureAwait(false);
    
                    if (server.IsConnected)
                    {
                        Console.WriteLine("Connected");
    
                        if (server.CanRead) {
                            // Read something...
                        }
    
                        if (server.CanWrite) {
                            // Write something...
    
                            await server.FlushAsync().ConfigureAwait(false);
    
                            server.WaitForPipeDrain();
                        }
    
                        server.Disconnect();
    
                        await HandleRequestAsync().ConfigureAwait(false);
                    }
                }
            }
    
            private static async Task SendRequestAsync(int index, int counter, int max)
            {
                using (NamedPipeClientStream client = new NamedPipeClientStream(".", "MyPipe", PipeDirection.InOut, PipeOptions.Asynchronous))
                {
                    await client.ConnectAsync().ConfigureAwait(false);
    
                    if (client.IsConnected)
                    {
                        Console.WriteLine($"Index: {index} Counter: {counter}");
    
                        if (client.CanWrite) {
                            // Write something...
    
                            await client.FlushAsync().ConfigureAwait(false);
    
                            client.WaitForPipeDrain();
                        }
    
                        if (client.CanRead) {
                            // Read something...
                        }
                    }
    
                    if (counter <= max) {
                        await SendRequestAsync(index, ++counter, max).ConfigureAwait(false);
                    }
                    else {
                        Console.WriteLine($"{index} Done!");
                    }
                }
            }
        }
    }
    

    假设:

    我期望它工作的方式是当我调用 SendRequestAsync 并发执行时我发出的所有请求,然后每个请求发出额外的请求,直到它到达 6,最后,它应该打印“完成!”。

    备注:
  • 我已经在 .NET Framework 4.7.1 和 .NET Core 2.0 上测试过它,我得到了相同的结果。
  • 客户端和服务器之间的 通信 总是本地 到机器的本地 客户端是 Web 应用程序,可以将某些作业(例如启动 3rd-party 进程)排队,并且服务器将作为 Windows 服务部署在同一台机器上作为部署这些客户端的 Web 服务器。
  • 最佳答案

    断开连接时,WaitForPipeDrain() 可能会因管道损坏而抛出 IOException

    如果这发生在您的服务器 Task 中,那么它将永远不会监听下一个连接,并且所有剩余的客户端连接都卡在 ConnectAsync() 上。

    如果这发生在其中一个客户端任务中,则它不会继续递归并增加该索引的计数器。

    如果您将 WaitForPipeDrain() 的调用包装在 try/catch 中,程序将永远运行,因为您的函数 HandleRequestAsync() 是无限递归的。

    简而言之,要让它发挥作用:

  • IOException
  • 处理 WaitForPipeDrain()
  • HandleRequestAsync() 必须在某个时候完成。
  • 10-08 20:01