zoukankan      html  css  js  c++  java
  • 21、Windows API 进程间通信,管道(Pipe)

        管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。

        管道分为匿名管道和命名管道。

        匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。

        命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。

    一、注意点

    1、常用API

    Pipes[2]

    [3,4]中也对这一部分进行了介绍。

    2、示例

    1)服务器端

    创建管道 >> 监听 >> 读写 >> 关闭

    CreateNamedPipe

    ConnectNamedPipe

    ReadFile/WriteFile

    DisconnectNamedPipe

    示例代码

    通过pipe进程间通信-服务器端
    通过pipe进程间通信
    **************************************/
    /* 头文件 */
    #include
    <windows.h>
    #include
    <stdio.h>
    #include
    <tchar.h>
    /* 常量 */
    #define PIPE_TIMEOUT 5000
    #define BUFSIZE 4096
    /* 结构定义 */
    typedef
    struct
    {
    OVERLAPPED oOverlap;
    HANDLE hPipeInst;
    TCHAR chRequest[BUFSIZE];
    DWORD cbRead;
    TCHAR chReply[BUFSIZE];
    DWORD cbToWrite;
    } PIPEINST,
    *LPPIPEINST;
    /* 函数声明 */
    VOID DisconnectAndClose(LPPIPEINST);
    BOOL CreateAndConnectInstance(LPOVERLAPPED);
    BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
    VOID GetAnswerToRequest(LPPIPEINST);
    VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
    VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
    /* 全局变量 */
    HANDLE hPipe;
    /* ************************************
    * int main(VOID)
    * 功能 pipe 通信服务端主函数
    *************************************
    */
    int main(VOID)
    {
    HANDLE hConnectEvent;
    OVERLAPPED oConnect;
    LPPIPEINST lpPipeInst;
    DWORD dwWait, cbRet;
    BOOL fSuccess, fPendingIO;

    // 用于连接操作的事件对象
    hConnectEvent = CreateEvent(
    NULL,
    // 默认属性
    TRUE, // 手工reset
    TRUE, // 初始状态 signaled
    NULL); // 未命名

    if (hConnectEvent == NULL)
    {
    printf(
    "CreateEvent failed with %d.\n", GetLastError());
    return 0;
    }
    // OVERLAPPED 事件
    oConnect.hEvent = hConnectEvent;

    // 创建连接实例,等待连接
    fPendingIO = CreateAndConnectInstance(&oConnect);

    while (1)
    {
    // 等待客户端连接或读写操作完成
    dwWait = WaitForSingleObjectEx(
    hConnectEvent,
    // 等待的事件
    INFINITE, // 无限等待
    TRUE);

    switch (dwWait)
    {
    case 0:
    // pending
    if (fPendingIO)
    {
    // 获取 Overlapped I/O 的结果
    fSuccess = GetOverlappedResult(
    hPipe,
    // pipe 句柄
    &oConnect, // OVERLAPPED 结构
    &cbRet, // 已经传送的数据量
    FALSE); // 不等待
    if (!fSuccess)
    {
    printf(
    "ConnectNamedPipe (%d)\n", GetLastError());
    return 0;
    }
    }

    // 分配内存
    lpPipeInst = (LPPIPEINST) HeapAlloc(GetProcessHeap(),0,sizeof(PIPEINST));
    if (lpPipeInst == NULL)
    {
    printf(
    "GlobalAlloc failed (%d)\n", GetLastError());
    return 0;
    }
    lpPipeInst
    ->hPipeInst = hPipe;

    // 读和写,注意CompletedWriteRoutine和CompletedReadRoutine的相互调用
    lpPipeInst->cbToWrite = 0;
    CompletedWriteRoutine(
    0, 0, (LPOVERLAPPED) lpPipeInst);

    // 再创建一个连接实例,以响应下一个客户端的连接
    fPendingIO = CreateAndConnectInstance(
    &oConnect);
    break;

    // 读写完成
    case WAIT_IO_COMPLETION:
    break;

    default:
    {
    printf(
    "WaitForSingleObjectEx (%d)\n", GetLastError());
    return 0;
    }
    }
    }
    return 0;
    }

    /* ************************************
    * CompletedWriteRoutine
    * 写入pipe操作的完成函数
    * 接口参见FileIOCompletionRoutine回调函数定义
    *
    * 当写操作完成时被调用,开始读另外一个客户端的请求
    *************************************
    */
    VOID WINAPI CompletedWriteRoutine(
    DWORD dwErr,
    DWORD cbWritten,
    LPOVERLAPPED lpOverLap)
    {
    LPPIPEINST lpPipeInst;
    BOOL fRead
    = FALSE;
    // 保存overlap实例
    lpPipeInst = (LPPIPEINST) lpOverLap;

    // 如果没有错误
    if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
    {
    fRead
    = ReadFileEx(
    lpPipeInst
    ->hPipeInst,
    lpPipeInst
    ->chRequest,
    BUFSIZE
    *sizeof(TCHAR),
    (LPOVERLAPPED) lpPipeInst,
    // 写读操作完成后,调用CompletedReadRoutine
    (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
    }
    if (! fRead)
    // 出错,断开连接
    DisconnectAndClose(lpPipeInst);
    }

    /* ************************************
    * CompletedReadRoutine
    * 读取pipe操作的完成函数
    * 接口参见FileIOCompletionRoutine回调函数定义
    *
    * 当读操作完成时被调用,写入回复
    *************************************
    */
    VOID WINAPI CompletedReadRoutine(
    DWORD dwErr,
    DWORD cbBytesRead,
    LPOVERLAPPED lpOverLap)
    {
    LPPIPEINST lpPipeInst;
    BOOL fWrite
    = FALSE;

    // 保存overlap实例
    lpPipeInst = (LPPIPEINST) lpOverLap;

    // 如果没有错误
    if ((dwErr == 0) && (cbBytesRead != 0))
    {
    // 根据客户端的请求,生成回复
    GetAnswerToRequest(lpPipeInst);
    // 将回复写入到pipe
    fWrite = WriteFileEx(
    lpPipeInst
    ->hPipeInst,
    lpPipeInst
    ->chReply, //将响应写入pipe
    lpPipeInst->cbToWrite,
    (LPOVERLAPPED) lpPipeInst,
    // 写入完成后,调用CompletedWriteRoutine
    (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
    }

    if (! fWrite)
    // 出错,断开连接
    DisconnectAndClose(lpPipeInst);
    }

    /* ************************************
    * VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
    * 功能 断开一个连接的实例
    * 参数 lpPipeInst,断开并关闭的实例句柄
    *************************************
    */
    VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
    {
    // 关闭连接实例
    if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
    {
    printf(
    "DisconnectNamedPipe failed with %d.\n", GetLastError());
    }
    // 关闭 pipe 实例的句柄
    CloseHandle(lpPipeInst->hPipeInst);
    // 释放
    if (lpPipeInst != NULL)
    HeapFree(GetProcessHeap(),
    0, lpPipeInst);
    }

    /* ************************************
    * BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
    * 功能 建立连接实例
    * 参数 lpoOverlap,用于overlapped IO的结构
    * 返回值 是否成功
    *************************************
    */
    BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
    {
    LPTSTR lpszPipename
    = TEXT("\\\\.\\pipe\\samplenamedpipe");
    // 创建named pipe
    hPipe = CreateNamedPipe(
    lpszPipename,
    // pipe 名
    PIPE_ACCESS_DUPLEX | // 可读可写
    FILE_FLAG_OVERLAPPED, // overlapped 模式
    // pipe模式
    PIPE_TYPE_MESSAGE | // 消息类型 pipe
    PIPE_READMODE_MESSAGE | // 消息读模式
    PIPE_WAIT, // 阻塞模式
    PIPE_UNLIMITED_INSTANCES, // 无限制实例
    BUFSIZE*sizeof(TCHAR), // 输出缓存大小
    BUFSIZE*sizeof(TCHAR), // 输入缓存大小
    PIPE_TIMEOUT, // 客户端超时
    NULL); // 默认安全属性
    if (hPipe == INVALID_HANDLE_VALUE)
    {
    printf(
    "CreateNamedPipe failed with %d.\n", GetLastError());
    return 0;
    }

    // 连接到新的客户端
    return ConnectToNewClient(hPipe, lpoOverlap);
    }

    /* ************************************
    * BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
    * 功能 建立连接实例
    * 参数 lpoOverlap,用于overlapped IO的结构
    * 返回值 是否成功
    *************************************
    */
    BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
    {
    BOOL fConnected, fPendingIO
    = FALSE;

    // 开始一个 overlapped 连接
    fConnected = ConnectNamedPipe(hPipe, lpo);

    if (fConnected)
    {
    printf(
    "ConnectNamedPipe failed with %d.\n", GetLastError());
    return 0;
    }
    switch (GetLastError())
    {
    // overlapped连接进行中.
    case ERROR_IO_PENDING:
    fPendingIO
    = TRUE;
    break;
    // 已经连接,因此Event未置位
    case ERROR_PIPE_CONNECTED:
    if (SetEvent(lpo->hEvent))
    break;
    // error
    default:
    {
    printf(
    "ConnectNamedPipe failed with %d.\n", GetLastError());
    return 0;
    }
    }
    return fPendingIO;
    }

    // TODO根据客户端的请求,给出响应
    VOID GetAnswerToRequest(LPPIPEINST pipe)
    {
    _tprintf( TEXT(
    "[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
    lstrcpyn( pipe
    ->chReply, TEXT("Default answer from server") ,BUFSIZE);
    pipe
    ->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
    }

    2)客户端

    打开命令管道,获得句柄 >> 写入数据 >> 等待回复

    WaitNamedPipe

    SetNamedPipeHandleState

    示例代码

    通过pipe进程间通信-客户端
    通过pipe进程间通信
    **************************************/
    /* 头文件 */
    #include
    <windows.h>
    #include
    <stdio.h>
    #include
    <conio.h>
    #include
    <tchar.h>
    /* 常量 */
    #define BUFSIZE 512
    /* ************************************
    * int main(VOID)
    * 功能 pipe 通信服务端主函数
    *************************************
    */
    int main(int argc, TCHAR *argv[])
    {
    HANDLE hPipe;
    LPTSTR lpvMessage
    =TEXT("Default message from client");
    TCHAR chBuf[BUFSIZE];
    BOOL fSuccess;
    DWORD cbRead, cbWritten, dwMode;
    LPTSTR lpszPipename
    = TEXT("\\\\.\\pipe\\samplenamedpipe");

    if( argc > 1 ) // 如果输入了参数,则使用输入的参数
    lpvMessage = argv[1];
    while (1)
    {
    // 打开一个命名pipe
    hPipe = CreateFile(
    lpszPipename,
    // pipe 名
    GENERIC_READ | GENERIC_WRITE, // 可读可写
    0, // 不共享
    NULL, // 默认安全属性
    OPEN_EXISTING, // 已经存在(由服务端创建)
    0, // 默认属性
    NULL);
    if (hPipe != INVALID_HANDLE_VALUE)
    break;

    // 如果不是 ERROR_PIPE_BUSY 错误,直接退出
    if (GetLastError() != ERROR_PIPE_BUSY)
    {
    printf(
    "Could not open pipe");
    return 0;
    }

    // 如果所有pipe实例都处于繁忙状态,等待2秒。
    if (!WaitNamedPipe(lpszPipename, 2000))
    {
    printf(
    "Could not open pipe");
    return 0;
    }
    }

    // pipe已经连接,设置为消息读状态
    dwMode = PIPE_READMODE_MESSAGE;
    fSuccess
    = SetNamedPipeHandleState(
    hPipe,
    // 句柄
    &dwMode, // 新状态
    NULL, // 不设置最大缓存
    NULL); // 不设置最长时间
    if (!fSuccess)
    {
    printf(
    "SetNamedPipeHandleState failed");
    return 0;
    }

    // 写入pipe
    fSuccess = WriteFile(
    hPipe,
    // 句柄
    lpvMessage, // 写入的内容
    (lstrlen(lpvMessage)+1)*sizeof(TCHAR), // 写入内容的长度
    &cbWritten, // 实际写的内容
    NULL); // 非 overlapped
    if (!fSuccess)
    {
    printf(
    "WriteFile failed");
    return 0;
    }

    do
    {
    // 读回复
    fSuccess = ReadFile(
    hPipe,
    // 句柄
    chBuf, // 读取内容的缓存
    BUFSIZE*sizeof(TCHAR), // 缓存大小
    &cbRead, // 实际读的字节
    NULL); // 非 overlapped

    if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
    break; //失败,退出

    _tprintf( TEXT(
    "%s\n"), chBuf ); // 打印读的结果
    } while (!fSuccess); // ERROR_MORE_DATA 或者成功则循环

    getch();
    //任意键退出
    // 关闭句柄
    CloseHandle(hPipe);
    return 0;
    }

    3I/O简介

        I/O模式不仅在进程间通信时使用,任何具有数据流形式的输入输出(包括文件输入输出、内核通信、网络输入输出等)都涉及I/O模式。

        异步( Asynchronous)和同步(Synchronous) I/O是两种基本的I/O模式

    同步I/O

    所谓同步I/O是指在调用ReadFileWriteFile等函数进行输入输出操作时,系统完成了输入输出ReadFileWriteFile才返回。在操作系统进行I/O操作的过程上,用户态线程不能执行,因此在同步I/O时,如果需要在I/O时进行其他操作就只能再开启线程。

    异步I/O

        异步I/O是在调用ReadFileWriteFile等函数后,函数立即返回,线程可以进行其他操作。剩下的I/O操作在系统内核中自动完成。那么在系统内核完成输入输出后,程序如何知道I/O是否已完成?

        一种方法,称作完成函数(Completion Routines),如果使用ReadFileExWriteFileEx等进行I/O,可以指定完成函数,所谓完成函数是指内核在完成I/O后,内核会回调这个函数。当完成函数被调用时,就指明内核已经完成了I/O,程序可以在这个函数中进行一个I/O完成后需要的操作(例如释放内存)

    参考

    [1] 精通Windows API 函数、接口、编程实例

    [2] http://msdn.microsoft.com/en-us/library/aa365137%28VS.85%29.aspx

    [3] http://www.cnblogs.com/mydomain/archive/2010/09/18/1830452.html

    [4] http://www.cnblogs.com/mydomain/archive/2010/09/04/1818266.html

  • 相关阅读:
    ACM的算法分类 2015-04-16 14:25 22人阅读 评论(0) 收藏
    初学Larevel 2014-08-21 11:24 90人阅读 评论(0) 收藏
    初学PHP&MySQL 2014-05-31 12:40 92人阅读 评论(0) 收藏
    codeforces 570 E. Pig and Palindromes (dp)
    codeforces 570 D. Tree Requests (dfs序)
    poj 2157 Maze (bfs)
    cf 570 C. Replacement (暴力)
    cf 570B B. Simple Game(构造)
    cf 570 A. Elections
    hdu 1429胜利大逃亡(续) (bfs+状态压缩)
  • 原文地址:https://www.cnblogs.com/mydomain/p/1931914.html
Copyright © 2011-2022 走看看