Pipeline 管道,进程间通信

在Windows平台下,C++的管道(Pipeline)通信主要分为匿名管道​(Anonymous Pipes)和命名管道​(Named Pipes)两种,分别适用于父子进程和无关进程间的通信。以下从原理、实现到代码示例详细说明:


⚙️ ​一、匿名管道(Anonymous Pipes)​

适用场景​:父子进程间的单向数据流(如重定向子进程输出)5
核心步骤​:

  1. 父进程调用CreatePipe创建读/写句柄。
  2. 通过STARTUPINFO将句柄传递给子进程。
  3. 子进程通过继承的句柄读写数据。

代码示例​(父进程读取子进程输出)5

#include <Windows.h>
#include <iostream>

int main() {
    HANDLE hReadPipe, hWritePipe;
    SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE }; // 允许句柄继承
    CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    si.hStdOutput = hWritePipe;  // 重定向子进程输出到管道写端
    si.dwFlags = STARTF_USESTDHANDLES;

    TCHAR cmd[] = TEXT("child.exe");
    CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    CloseHandle(hWritePipe);  // 父进程关闭写端

    char buffer[4096];
    DWORD bytesRead;
    while (ReadFile(hReadPipe, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) {
        std::cout.write(buffer, bytesRead);  // 打印子进程输出
    }

    CloseHandle(hReadPipe);
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    return 0;
}

关键点​:

  • 子进程需将输出重定向到管道写端(si.hStdOutput = hWritePipe)。
  • 父进程必须关闭未使用的句柄(如写端),否则读操作会阻塞5

🔍 ​二、命名管道(Named Pipes)​

适用场景​:无关进程的双向通信(如C++服务端与C#客户端交互)1,6,7

1. 同步通信示例(服务端-客户端)​

服务端代码​(创建管道并等待连接)2,3

#include <Windows.h>
#include <iostream>

int main() {
    // 创建命名管道(双向、消息模式)
    HANDLE hPipe = CreateNamedPipe(
        L"\\\\.\\pipe\\MyPipe",          // 管道名称
        PIPE_ACCESS_DUPLEX,              // 双向通信
        PIPE_TYPE_MESSAGE | PIPE_WAIT,   // 消息模式 + 阻塞
        PIPE_UNLIMITED_INSTANCES,        // 最大实例数
        4096, 4096,                      // 输入/输出缓冲区大小
        0, NULL);                        // 默认超时和安全属性

    if (hPipe == INVALID_HANDLE_VALUE) {
        std::cerr << "CreateNamedPipe failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "等待客户端连接..." << std::endl;
    if (ConnectNamedPipe(hPipe, NULL)) { // 阻塞等待客户端
        const char* msg = "Hello from Server!";
        DWORD bytesWritten;
        WriteFile(hPipe, msg, strlen(msg) + 1, &bytesWritten, NULL);  // 发送数据
        std::cout << "数据已发送" << std::endl;
    }
    CloseHandle(hPipe);
    return 0;
}

客户端代码​(连接管道并读取数据)2,4

#include <Windows.h>
#include <iostream>

int main() {
    if (WaitNamedPipe(L"\\\\.\\pipe\\MyPipe", NMPWAIT_WAIT_FOREVER)) { // 等待管道可用
        HANDLE hPipe = CreateFile(
            L"\\\\.\\pipe\\MyPipe",     // 管道名称
            GENERIC_READ | GENERIC_WRITE, 
            0, NULL, OPEN_EXISTING, 0, NULL);

        if (hPipe != INVALID_HANDLE_VALUE) {
            char buffer[256];
            DWORD bytesRead;
            ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL); // 读取数据
            std::cout << "收到服务端消息: " << buffer << std::endl;
            CloseHandle(hPipe);
        }
    }
    return 0;
}
2. 异步通信(高性能场景)​

使用OVERLAPPED结构实现非阻塞I/O,避免线程阻塞9

// 服务端异步读取示例
HANDLE hPipe = CreateNamedPipe(..., FILE_FLAG_OVERLAPPED, ...); // 启用异步模式
OVERLAPPED ov = {0};
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  // 创建事件对象

if (ConnectNamedPipe(hPipe, &ov)) {  // 异步连接
    DWORD bytesRead;
    char buffer[256];
    if (ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, &ov)) {
        // 读取成功(可能立即完成)
    } else if (GetLastError() == ERROR_IO_PENDING) {
        WaitForSingleObject(ov.hEvent, INFINITE);  // 等待操作完成
    }
}
CloseHandle(ov.hEvent);

优势​:

  • 通过事件对象(hEvent)通知I/O完成,提升并发性能9
  • 适合服务端处理多客户端请求的场景。

⚖️ ​三、关键问题与解决方案

问题原因解决方案
连接阻塞客户端未启动或管道名称错误使用WaitNamedPipe检测管道可用性4
数据读写失败缓冲区不足或模式不匹配(字节/消息)统一PIPE_TYPE_MESSAGEPIPE_READMODE_MESSAGE3
句柄泄漏未关闭管道或事件对象确保CloseHandle释放所有资源9
跨语言通信乱码编码不一致(如C++窄字节 vs C# UTF-8)双方统一使用UTF-8编码6,7

💎 ​四、总结与建议

  1. 选型原则​:

    • 匿名管道​:仅需重定向子进程输入/输出时使用(如命令行工具)。
    • 命名管道​:无关进程双向通信的首选(尤其C++与C#交互)6,7
    • 异步模式​:高并发场景必备(如服务端程序)9
  2. 最佳实践​:

    • 名称规范​:管道名格式必须为\\.\pipe\Pipename3
    • 超时控制​:设置NMPWAIT_USE_DEFAULT避免无限等待4
    • 错误处理​:检查GetLastError()并重试关键操作(如连接断开时重建管道)。
  3. 扩展场景​:

    • 跨网络通信:改用套接字​(Sockets)7
    • 大规模数据:结合共享内存提升传输效率(需信号量同步)4

通过合理选择管道类型和同步机制,Windows管道通信可高效支撑进程协作需求。完整代码示例可参考:命名管道完整实现2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值