.NET 跨應用程式通訊處理: Named Pipe - II

先回顧一下在 "前一篇" 當中有提到基本的使用架構:

+--------------------+                          +--------------------+
|   Server Process   |         Named Pipe       |   Client Process   |
|                    | <----------------------> |                    |
|   NamedPipeServer  |       \\.\pipe\demo      |   NamedPipeClient  |
+--------------------+                          +--------------------+

 

其中所列舉的範例程式,在 Server 端的 NamedPipeServerStream 使用中,PipeDirection是直接建立 InOut 的。

所以當 Client 同樣也透過 PipeDirection是 InOut 的 NamedPipeClientStream 連線成功後,可以透過一條 Pipe 讓 Server / Client 之間互相拋資料。

如果把該篇的 Server / Client 範例再做點延伸,做成可透過 Console 輸入讓 Server / Client 之間互相 Chat 的範例。


 

首先,皆在 Server / Client 中設計了兩個 Task,其中一個 Task 透過 StreamReader 不斷的在 Pipe 上讀取資料;另一個 Task 透過 StreamWriter 不斷的接收從 Console 輸入的資料後,寫到 Pipe 當中。

並在其中設計了一個特殊的關鍵字 "exit"。

只要 Server/Client 有任一端有在 Console 中輸入了 "exit" 的文字,並且送出,那 Server / Client 雙方則將停止程式的運作。

 

下列為 Server 端的程式:

class ChatPipeServer
{
	static async Task Main()
	{
		using var server = new NamedPipeServerStream("demoPipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

		Console.WriteLine("Waiting for client...");
		await server.WaitForConnectionAsync();

		Console.WriteLine("Client connected.");

		using var reader = new StreamReader(server, new UTF8Encoding(false));
		using var writer = new StreamWriter(server, new UTF8Encoding(false)) { AutoFlush = true };
		
		var readTask = Task.Run(async () =>
		{
			var buffer = new char[1024];
			while (server.IsConnected)
			{
				var count = await reader.ReadAsync(buffer, 0, buffer.Length);
				if (count == 0)
					break;

				var message = new string(buffer, 0, count);
				Console.WriteLine($"Client: {message}");
				Console.Write("> ");

				if (message is null || message.Equals("exit", StringComparison.OrdinalIgnoreCase))
					break;
			}
		});

		var writeTask = Task.Run(async () =>
		{
			while (server.IsConnected)
			{
				Console.Write("> ");
				var input = Console.ReadLine();

				await writer.WriteAsync(input);
				if (input is null || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
				{
					Console.WriteLine("User ask \"EXIT\".");
					break;
				}
			}
		});

		await Task.WhenAny(readTask, writeTask);

		Console.WriteLine("Server is closed.");
	}
}

 

下列為 Client 端的程式:

class ChatPipeClient
{
	static async Task Main()
	{
		using var client = new NamedPipeClientStream(".", "demoPipe", PipeDirection.InOut, PipeOptions.Asynchronous);

		Console.WriteLine("Connecting...");
		await client.ConnectAsync();

		Console.WriteLine("Connected to server.");

		using var reader = new StreamReader(client, new UTF8Encoding(false));
		using var writer = new StreamWriter(client, new UTF8Encoding(false)) { AutoFlush = true };

		var readTask = Task.Run(async () =>
		{
			var buffer = new char[1024];
			while (client.IsConnected)
			{
				var count = await reader.ReadAsync(buffer, 0, buffer.Length);
				if (count == 0)
					break;
				
				var message = new string(buffer, 0, count);
				Console.WriteLine($"Server: {message}");
				Console.Write("> ");

				if (message is null || message.Equals("exit", StringComparison.OrdinalIgnoreCase))
					break;
			}
		});

		var writeTask = Task.Run(async () =>
		{
			while (client.IsConnected)
			{
				Console.Write("> ");
				var input = Console.ReadLine();

				await writer.WriteAsync(input);
				if (input is null || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
				{
					Console.WriteLine("User ask \"EXIT\".");
					break;
				}
			}
		});

		await Task.WhenAny(readTask, writeTask);

		Console.WriteLine("Client is closed.");
	}
}

 

 

透過 LinqPad 做上述的範例程式的展示 (下圖左邊為 Server 端;右邊為 Client 端):

Server / Client 雙方已經透過 Pipe 連線上,展示的步驟大約如下:

  1. Client 先發送 "Hi" 的資訊。
  2. Server 收到後且顯示在 Console 當中。
  3. Server 發送 "Hello" 的資訊。
  4. Client 收到後且顯示在 Console 當中。
  5. Server 發送 "What's your name?" 的資訊。
  6. Client 收到後顯示在 Console 當中。
  7. Client 發送 "Client, and you?"。
  8. Server 收到後顯示在 Console 當中。
  9. Server 發送 "Server!"。
  10. Client 收到後顯示在 Console 當中。
  11. Client 發送 "Ok"。
  12. Server 收到後顯示在 Console 當中
  13. Server 發送 "Okay"。
  14. Client 發送 "Exit"。
  15. Server / Client 雙方各自結束程式執行。

 

上述 Chat 範例的運作流程簡單圖解:

 

從 Server 輸入到 Client 顯示的流程:

   Server Console 輸入
          │
          ▼
  StreamWriter.Write()
          │
          ▼
         Pipe
          │
          ▼
Client StreamReader.Read()
          │
          ▼
  Client Console 顯示

 

反之,從 Client 輸入到 Server 顯示的流程:

   Client Console 輸入
          │
          ▼
  StreamWriter.Write()
          │
          ▼
         Pipe
          │
          ▼
Server StreamReader.Read()
          │
          ▼
  Server Console 顯示

 

所以,運用 Pipe 連線實踐兩個程式之間的互相拋資料,這樣的概念理論上是沒什麼太大的問題的。

 


 


I'm a Microsoft MVP - Developer Technologies (From 2015 ~).
 

MVP_Logo



I focus on the following topics: Xamarin Technology, Azure, Mobile DevOps, and Microsoft EM+S.

If you want to know more about them, welcome to my website:
https://jamestsai.tw 


本部落格文章之圖片相關後製處理皆透過 Techsmith 公司 所贊助其授權使用之 "Snagit" 與 "Snagit Editor" 軟體製作。