进程通信专题

2、管道

1、课程目标

  1. 理解管道的类型和特点
  2. 掌握匿名管道和命名管道的使用
  3. 学会双向通信的实现
  4. 理解管道在安全领域的应用

2、名词解释

术语 英文 说明
匿名管道 Anonymous Pipe 父子进程间的单向通信
命名管道 Named Pipe 可跨进程/网络的双向通信
消息模式 Message Mode 按消息边界读取
字节模式 Byte Mode 按字节流读取

3、代码实现

3.1、示例1:匿名管道

#include <windows.h>
#include <stdio.h>

void AnonymousPipeDemo() {
    HANDLE hReadPipe, hWritePipe;
    SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, TRUE};
    
    // 创建管道
    if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
        printf("CreatePipe failed\n");
        return;
    }
    
    // 创建子进程
    STARTUPINFO si = {sizeof(si)};
    PROCESS_INFORMATION pi;
    
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = hReadPipe;
    si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    TCHAR cmdLine[] = TEXT("child.exe");
    CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    
    // 父进程写入
    const char* msg = "Hello from parent";
    DWORD written;
    WriteFile(hWritePipe, msg, strlen(msg), &written, NULL);
    
    CloseHandle(hWritePipe);
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    CloseHandle(hReadPipe);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

3.2、示例2:命名管道服务端

#include <windows.h>
#include <stdio.h>

void NamedPipeServer() {
    HANDLE hPipe = CreateNamedPipe(
        TEXT("\\\\.\\pipe\\MyPipe"),
        PIPE_ACCESS_DUPLEX,           // 双向
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
        1,                            // 最大实例数
        4096, 4096,                   // 缓冲区大小
        0,                            // 默认超时
        NULL                          // 安全属性
    );
    
    if (hPipe == INVALID_HANDLE_VALUE) {
        printf("CreateNamedPipe failed: %d\n", GetLastError());
        return;
    }
    
    printf("Waiting for client...\n");
    
    // 等待客户端连接
    if (ConnectNamedPipe(hPipe, NULL) || GetLastError() == ERROR_PIPE_CONNECTED) {
        char buffer[256];
        DWORD bytesRead, bytesWritten;
        
        // 读取客户端消息
        ReadFile(hPipe, buffer, sizeof(buffer)-1, &bytesRead, NULL);
        buffer[bytesRead] = '\0';
        printf("Received: %s\n", buffer);
        
        // 发送响应
        const char* response = "Hello from server";
        WriteFile(hPipe, response, strlen(response), &bytesWritten, NULL);
        
        DisconnectNamedPipe(hPipe);
    }
    
    CloseHandle(hPipe);
}

3.3、示例3:命名管道客户端

#include <windows.h>
#include <stdio.h>

void NamedPipeClient() {
    HANDLE hPipe;
    
    // 等待管道可用
    WaitNamedPipe(TEXT("\\\\.\\pipe\\MyPipe"), NMPWAIT_WAIT_FOREVER);
    
    // 连接管道
    hPipe = CreateFile(
        TEXT("\\\\.\\pipe\\MyPipe"),
        GENERIC_READ | GENERIC_WRITE,
        0, NULL,
        OPEN_EXISTING,
        0, NULL
    );
    
    if (hPipe == INVALID_HANDLE_VALUE) {
        printf("Connect failed: %d\n", GetLastError());
        return;
    }
    
    // 设置消息模式
    DWORD mode = PIPE_READMODE_MESSAGE;
    SetNamedPipeHandleState(hPipe, &mode, NULL, NULL);
    
    // 发送消息
    const char* message = "Hello from client";
    DWORD bytesWritten, bytesRead;
    WriteFile(hPipe, message, strlen(message), &bytesWritten, NULL);
    
    // 读取响应
    char buffer[256];
    ReadFile(hPipe, buffer, sizeof(buffer)-1, &bytesRead, NULL);
    buffer[bytesRead] = '\0';
    printf("Response: %s\n", buffer);
    
    CloseHandle(hPipe);
}

3.4、示例4:远程管道连接

#include <windows.h>
#include <stdio.h>

void RemotePipeClient(const char* server) {
    char pipeName[256];
    sprintf(pipeName, "\\\\%s\\pipe\\MyPipe", server);
    
    HANDLE hPipe = CreateFileA(
        pipeName,
        GENERIC_READ | GENERIC_WRITE,
        0, NULL,
        OPEN_EXISTING,
        0, NULL
    );
    
    // ... 与本地管道使用相同
}

4、课后作业

  1. 基础练习:实现父子进程间的管道通信
  2. 双向通信:使用命名管道实现请求-响应模式
  3. 多客户端:实现支持多客户端的管道服务端
  4. 安全应用:分析SMB命名管道的安全风险