命名管道(Named Pipes)深度解析(C#—C++应用间交互)

命名管道(Named Pipes)深度解析(C#—C++应用间交互)

管道

一、核心概念

命名管道(Named Pipes)是Windows系统中一种进程间通信(IPC)*机制,支持*跨进程甚至跨网络的双向数据流传输。其核心特点如下:

  1. 命名唯一性:通过全局唯一的管道名称(如\\.\pipe\MyPipe)标识通信端点。
  2. 双工通信:支持同步/异步的读写操作,服务端与客户端可同时收发数据。
  3. 安全控制:可设置ACL(访问控制列表),限制特定用户或进程的访问权限。

二、C#实现模型

在C#中,通过System.IO.Pipes命名空间实现,核心类为:

  • NamedPipeServerStream:服务端管道,监听并接受客户端连接。
  • NamedPipeClientStream:客户端管道,主动连接服务端。
通信流程
 
 
 
 
服务端创建管道
等待客户端连接
客户端连接管道
数据交换

三、基础代码示例

1. 服务端实现(接收数据)
 
 
 
 
 
 
 
 
using System.IO.Pipes;
using System.Text;

// 服务端代码 
var pipeName = "UnityDataPipe";
using (var server = new NamedPipeServerStream(
    pipeName,
    PipeDirection.InOut,    // 双向通信 
    maxNumberOfServerInstances: 1,
    PipeTransmissionMode.Message)) // 按消息分块 
{
    Console.WriteLine("等待客户端连接...");
    server.WaitForConnection();
    
    // 读取数据 
    byte[] buffer = new byte[1024];
    int bytesRead = server.Read(buffer, 0, buffer.Length);
    string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
    Console.WriteLine($"收到消息: {message}");
    
    // 发送响应 
    byte[] response = Encoding.UTF8.GetBytes("ACK");
    server.Write(response, 0, response.Length);
}
 
2. 客户端实现(发送数据)
 
 
 
 
 
 
 
 
using (var client = new NamedPipeClientStream(
    ".",
    "UnityDataPipe",
    PipeDirection.InOut))
{
    client.Connect(3000); // 超时3秒 
    
    // 发送数据 
    string message = "点云数据路径: D:/data.pcd"; 
    byte[] data = Encoding.UTF8.GetBytes(message);
    client.Write(data, 0, data.Length);
    
    // 读取响应 
    byte[] response = new byte[256];
    int bytesRead = client.Read(response, 0, response.Length);
    Console.WriteLine($"服务端响应: {Encoding.UTF8.GetString(response, 0, bytesRead)}");
}
 
3. Unity端与C++应用通信

 

C#服务端

 
 
 
x
 
 
 
 
using System;
using System.IO;
using UnityEngine;
using System.IO.Pipes;
using System.Threading;
using System.Text;
using System.Threading.Tasks;

public class NamedPathPipeServer
{
    // 配置参数 
    public string pipeName = "UnityFilePathPipe";
    public int bufferSize = 4096; // 路径字符串通常较短 

    private NamedPipeServerStream _server;
    private CancellationTokenSource _cts;
    private string _receivedPath = null;
    private object _pathLock = new object();

    public void Start() => StartServer();
    public void OnDestroy() => StopServer();
    public void OnApplicationQuit() => StopServer();

    // 启动服务端 
    private void StartServer()
    {
        _cts = new CancellationTokenSource();
        Task.Run(() => ListenForPaths(_cts.Token), _cts.Token);
    }

    // 监听循环 
    private async Task ListenForPaths(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            try 
            {
                using (_server = new NamedPipeServerStream(
                    pipeName,
                    PipeDirection.In,
                    1,
                    PipeTransmissionMode.Message, // 按消息分块 
                    PipeOptions.Asynchronous))
                {
                    await _server.WaitForConnectionAsync(token);
                    byte[] buffer = new byte[bufferSize];
                    int bytesRead = await _server.ReadAsync(buffer, 0, buffer.Length, token);
                    string path = Encoding.UTF8.GetString(buffer, 0, bytesRead);

                    lock (_pathLock)
                    {
                        _receivedPath = path;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"管道异常: {e.Message}");
            }
        }
    }

    // Unity主线程处理 
    public void Update()
    {
        string pathToLoad = null;
        lock (_pathLock)
        {
            if (!string.IsNullOrEmpty(_receivedPath))
            {
                pathToLoad = _receivedPath;
                _receivedPath = null;
            }
        }

        if (pathToLoad != null)
        {
            Debug.Log($"收到点云路径: {pathToLoad}");
            LoadPointCloudFromPath(pathToLoad);
        }
    }

    private void LoadPointCloudFromPath(string filePath)
    {
        // 此处实现文件读取逻辑(示例伪代码)
        if (File.Exists(filePath))
        {
            byte[] data = File.ReadAllBytes(filePath);
            Vector3[] points = ParsePointCloud(data); // 解析为坐标数组 
            RenderPointCloud(points);
        }
        else 
        {
            Debug.LogError($"文件不存在: {filePath}");
        }
    }
    
    // 解析点云 
    private Vector3[] ParsePointCloud(byte[] data)
    {
        return new Vector3[0]; // 示例代码,实际解析请根据文件格式自行实现
    }

    // 渲染点云 
    private void RenderPointCloud(Vector3[] points)
    {
        // 此处实现渲染逻辑(示例伪代码)
        foreach (var point in points)
        {
            Debug.DrawRay(point, Vector3.up, Color.red, 0.1f, false);
        }
    }

    private void StopServer()
    {
        _cts?.Cancel();
        _server?.Close();
    }
}
 

 

C++客户端

 
 
 
xxxxxxxxxx
 
 
 
 
void SendPathToUnity(const std::string& path) {
    HANDLE hPipe = CreateFileA(
        "\\\\.\\pipe\\UnityFilePathPipe",
        GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hPipe == INVALID_HANDLE_VALUE) {
        // 错误处理 
        return;
    }

    DWORD bytesWritten;
    WriteFile(hPipe, path.c_str(), path.size(), &bytesWritten, NULL);
    CloseHandle(hPipe);
}
 

 


四、高级特性与最佳实践

1. 异步通信模式
 
 
 
 
 
 
 
 
// 服务端异步等待连接 
await server.WaitForConnectionAsync();

// 异步读写 
byte[] buffer = new byte[4096];
int bytesRead = await server.ReadAsync(buffer, 0, buffer.Length);
await server.WriteAsync(responseBuffer, 0, responseBuffer.Length);
 
2. 多客户端支持
 
 
 
 
 
 
 
 
// 服务端循环处理多个客户端 
while (true)
{
    using (var server = new NamedPipeServerStream(...))
    {
        await server.WaitForConnectionAsync();
        Task.Run(() => HandleClient(server)); // 为每个客户端创建独立任务 
    }
}
 
3. 消息分帧协议
  • 长度前缀法:发送数据前附加4字节长度头。
 
 
 
 
 
 
 
 
// 发送端 
byte[] data = ...;
byte[] lengthHeader = BitConverter.GetBytes(data.Length);
client.Write(lengthHeader, 0, 4);
client.Write(data, 0, data.Length);

// 接收端 
byte[] header = new byte[4];
await stream.ReadAsync(header, 0, 4);
int length = BitConverter.ToInt32(header, 0);
byte[] payload = new byte[length];
await stream.ReadAsync(payload, 0, length);
 
4. 安全性控制
 
 
 
 
 
 
 
 
// 设置管道权限(仅允许当前用户)
var pipeSecurity = new PipeSecurity();
pipeSecurity.AddAccessRule(new PipeAccessRule(
    WindowsIdentity.GetCurrent().User,
    PipeAccessRights.ReadWrite,
    AccessControlType.Allow));

var server = new NamedPipeServerStream(
    pipeName,
    PipeDirection.InOut,
    1,
    PipeTransmissionMode.Message,
    PipeOptions.None,
    4096, 4096,
    pipeSecurity);
 

五、性能优化策略

优化方向实现方法
缓冲区管理 使用固定大小缓冲区池(避免频繁内存分配)
批量传输 合并小数据包,减少系统调用次数(如每100ms发送一次数据)
零拷贝技术 通过MemoryMappedFile共享内存区域(需配合事件同步)
多线程处理 分离读写线程,利用BlockingCollection实现生产者-消费者模型

六、适用场景与局限性

适用场景
  • Unity与本地C++程序实时数据交换
  • 需要严格权限控制的内部进程通信
  • 高吞吐量但低延迟的本地IPC需求
局限性
  • 跨平台限制:原生命名管道主要支持Windows,Linux/macOS需通过第三方库(如Mono.Posix
  • 网络延迟:跨网络通信时性能低于专用网络协议(如gRPC)

七、与其他IPC机制对比

机制延迟吞吐量跨平台复杂度
命名管道
TCP Socket
共享内存 极低 极高
gRPC

📚 扩展学习资源

  1. 微软官方文档:NamedPipeServerStream

通过合理使用命名管道,开发者可在C#项目中实现高效可靠的本地进程通信,尤其适用于需要高实时性的数据交换场景。

posted @   世纪末の魔术师  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
  1. 1 ありがとう··· KOKIA
ありがとう··· - KOKIA
00:00 / 00:00
An audio error has occurred.

作词 : KOKIA

作曲 : KOKIA

编曲 : 日向敏文

作词 : KOKIA

作曲 : KOKIA

誰もが気付かぬうちに

誰もが気付かぬうちに

何かを失っている

フッと気付けばあなたはいない

思い出だけを残して

せわしい時の中

言葉を失った人形達のように

街角に溢れたノラネコのように

声にならない叫びが聞こえてくる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じていたい

思い出はせめてもの慰め

いつまでもあなたはここにいる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じてたい

点击右上角即可分享
微信分享提示