2025-09-15
通讯
00

目录

引言
命名管道简介
命名管道的核心作用:
命名管道的典型应用场景:
与其他IPC方式的对比:
示例
NamedPipeClient
NamedPipeServer
测试

引言

前段时间,因新项目是的 x64 的,而老项目是 x86 的,新老项目又有一个共用的底层库是 x86 的,本来我新项目编译为any CPU 时在调用公共底层库也是没问题的,但是还有一个用于连接仪器的外部 c++ 库文件是 x64 ,此时 any cpu 的新项目, x86 底层库,加一个 x64 的外部 c++ 库,此时这个 c++ 库文件就无法使用了,为了糅合这种情况,也为了把公共底层库剥离出来(因为后续新项目仍然会用该库),想办法采用进程间通讯的方式,来解决这一问题

命名管道简介

注意

以下内容来自AI

命名管道(Named Pipe)是一种用于进程间通信(IPC,Inter-Process Communication) 的机制,主要作用是实现同一台计算机上不同进程之间(或网络中不同计算机的进程之间)的数据传输和交互。它是操作系统提供的一种高级IPC方式,相比其他通信方式(如信号、共享内存等)。

命名管道的核心作用:

  1. 本地进程间通信
    最常见的用途是在同一台计算机上的不同进程之间传递数据。例如:

    • 一个应用程序(如视频编辑软件)可以通过命名管道向另一个进程(如渲染引擎)发送任务指令。
    • 后台服务(如数据库服务)可以通过命名管道接收客户端程序(如数据库管理工具)的请求并返回结果。
  2. 跨网络通信
    命名管道支持网络通信(如Windows的"命名管道网络协议"),允许不同计算机上的进程通过网络进行数据交互。例如:

    • 客户端程序可以通过网络连接到远程服务器的命名管道,实现跨机器的数据传输。
    • 相比Socket,命名管道在Windows环境下的网络通信更简单,无需手动处理底层TCP/IP细节。
  3. 双向通信支持
    命名管道可以配置为双向通信模式PipeDirection.InOut),允许通信双方既发送数据又接收数据,适合需要交互对话的场景(如客户端与服务端的命令交互)。

  4. 可靠的数据传输
    命名管道提供基于"流"或"消息"的传输模式,确保数据按顺序、完整地传递,避免数据丢失或错乱。例如:

    • 采用PipeTransmissionMode.Message模式时,接收方可以按消息边界准确拆分数据,无需手动处理分包。
  5. 安全性控制
    支持通过权限设置(如PipeSecurity)限制访问者,确保只有授权进程能连接或读写数据,适合传输敏感信息。

命名管道的典型应用场景:

  • 客户端-服务端架构(如数据库客户端与服务端通信)。
  • 桌面应用与后台服务的交互(如杀毒软件前台界面与后台监控进程)。
  • 复杂应用的模块化拆分(将功能拆分为多个进程,通过管道协作)。
  • 跨网络的轻量级数据交换(相比HTTP/Socket更简单的局域网通信)。

与其他IPC方式的对比:

  • 相比共享内存:命名管道更易用,无需手动处理同步问题,但传输效率略低。
  • 相比Socket:在本地通信时更高效,无需经过网络协议栈;网络通信时封装了底层细节,使用更简单。
  • 相比消息队列:命名管道是点对点通信,更适合实时交互,而消息队列适合异步、一对多的场景。

总之,命名管道是一种平衡了易用性、安全性和效率的IPC机制,尤其在Windows环境下被广泛用于进程间的数据交互。

示例

NamedPipeClient

代码如下:

csharp
using System; using System.IO; using System.IO.Pipes; using System.Text; using System.Threading.Tasks; class PipeClient { static void Main(string[] args) { Console.WriteLine("命名管道客户端已启动..."); Console.WriteLine("尝试连接到服务端..."); // 创建命名管道客户端流,连接到本地计算机上的管道 var clientStream = new NamedPipeClientStream( ".", // 服务器名称,"." 表示本地计算机 "MyNamedPipe", // 管道名称,必须与服务端一致 PipeDirection.InOut, // 双向通信 PipeOptions.Asynchronous); try { // 连接到服务端,超时时间设置为5秒 clientStream.Connect(5000); Console.WriteLine("已成功连接到服务端!"); // 使用StreamReader和StreamWriter简化读写操作 var reader = new StreamReader(clientStream, Encoding.UTF8); var writer = new StreamWriter(clientStream, Encoding.UTF8); string serverMessage; // 循环发送消息到服务端 while (true) { Console.Write("请输入要发送给服务端的消息(输入exit退出): "); string clientMessage = Console.ReadLine(); // 发送消息到服务端 writer.WriteLine(clientMessage); writer.Flush(); // 如果客户端发送"exit",则退出通信 if (clientMessage.Equals("exit", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("客户端请求断开连接..."); break; } // 等待服务端回应 serverMessage = reader.ReadLine(); if (serverMessage == null) { Console.WriteLine("服务端已断开连接"); break; } Console.WriteLine($"收到服务端消息: {serverMessage}"); // 如果服务端发送"exit",则退出通信 if (serverMessage.Equals("exit", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("服务端请求断开连接..."); break; } } } catch (TimeoutException) { Console.WriteLine("连接超时,无法连接到服务端"); } catch (IOException ex) { Console.WriteLine($"通信错误: {ex.Message}"); } Console.WriteLine("客户端已停止,按任意键退出..."); Console.ReadKey(); } }

NamedPipeServer

代码如下:

csharp
using System; using System.IO; using System.IO.Pipes; using System.Text; using System.Threading.Tasks; class PipeServer { static void Main(string[] args) { Console.WriteLine("命名管道服务端已启动..."); Console.WriteLine("等待客户端连接..."); // 创建命名管道服务器流,使用 "." 表示本地计算机 var serverStream = new NamedPipeServerStream( "MyNamedPipe", // 管道名称 PipeDirection.InOut, // 双向通信 NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous); // 等待客户端连接 serverStream.WaitForConnection(); Console.WriteLine("客户端已连接!"); // 使用StreamReader和StreamWriter简化读写操作 var reader = new StreamReader(serverStream, Encoding.UTF8); var writer = new StreamWriter(serverStream, Encoding.UTF8); try { string clientMessage; // 循环接收客户端消息 while (true) { clientMessage = reader.ReadLine(); Console.WriteLine($"收到客户端消息: {clientMessage}"); // 如果客户端发送"exit",则退出通信 if (clientMessage.Equals("exit", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("客户端请求断开连接..."); break; } // 向客户端发送回应 Console.Write("请输入要发送给客户端的消息: "); string serverMessage = Console.ReadLine(); writer.WriteLine(serverMessage); writer.Flush(); // 如果服务端发送"exit",则退出通信 if (serverMessage.Equals("exit", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("服务端请求断开连接..."); break; } } } catch (IOException ex) { Console.WriteLine($"通信错误: {ex.Message}"); } Console.WriteLine("连接已关闭"); Console.WriteLine("服务端已停止,按任意键退出..."); Console.ReadKey(); } }

测试

将上述两个控制台程序编译后,直接运行两个程序后,会自动连接,如下图:

image.png

在开发和测试过程中,也发现一些问题:

  • 跨平台兼容性差,Windows 与 Linux/macOS 实现差异大;
  • 网络通信局限,仅适用于局域网 SMB 环境,难用于互联网;
  • 并发连接数有限,高并发需手动管理多实例;
  • 性能不及共享内存,数据传输有内核缓冲区开销;
  • 无内置消息队列,需手动实现缓存、重试;
  • 调试难,连接失败原因模糊,异常易残留资源;
  • 安全配置门槛高,权限规则需深入系统安全知识。

所以,重点关注第二节中命名管道的核心作用描述中的场景,仅在这些场景下,推荐使用命名管道进行进程间通讯。

本文作者:Peter.Pan

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!