进程间通信 -- 管道

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

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

  • 匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。
  • 命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。
匿名管道

创建匿名管道:
定义安全属性结构体:

    SECURITY_ATTRIBUTES sa;
    sa.bInheritHandle = TRUE;//表示可被子进程所继承 
    sa.lpSecurityDescriptor = NULL; //安全描述符号一般都设置成NULL,即默认描述符
    sa.nLength = sizeof(SECURITY_ATTRIBUTES); //管道长度

创建管道:

    BOOL WINAPI CreatePipe(
      PHANDLE hReadPipe,//读取端句柄
      PHANDLE hWritePipe,//输入端句柄
      LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全属性
      DWORD nSize// 管道的缓冲区容量,NULL表示默认大小
    );

读取管道内数据:

   BOOL ReadFile(
     HANDLE hFile,//句柄,可以是标准输入输出流或文件或管道
     LPVOID lpBuffer,  //读取的数据写入缓冲区
     DWORD nNumberOfBytesToRead,//指定读取的字节数
     LPDWORD lpNumberOfBytesRead,//实际读取的字节数
     LPOVERLAPPED lpOverlapped//用于异步操作,一般置为NULL
   );

向管道内写入数据:

    BOOL WriteFile(
      HANDLE hFile,//句柄,同上
      LPCVOID lpBuffer,//指定待写入的数据
      DWORD nNumberOfBytesToWrite,//写入的数据量
      LPDWORDlp NumberOfBytesWritten,//实际要写的数据量
      LPOVERLAPPED lpOverlapped//一般置为NULL
    );

为实现父子进程间的通信,需要对子进程的管道进行重定向:

    STARTUPINFO si; 
    si.hStdInput   = hPipeInputRead;   //输入由标准输入 -> 从管道中读取  
    si.hStdOutput  = hPipeOutputWrite; //输出由标准输出 -> 输出到管道 
命名管道

创建命名管道:
命名管道有点类似我们常听见的服务器端和客户端,命名管道有自己的名字,首先要指定管道名,管道名遵循的格式为:
\\.\pipe\pipename。最多可达256个字符的长度,而且不区分大小写
例如:"\\.\pipe\Name_pipe_demon_get"

服务器端创建命名管道

        HANDLE WINAPI CreateNamedPipe(  

          LPCTSTRlpName,//管道名
          DWORD dwOpenMode,//管道打开方式
          //PIPE_ACCESS_DUPLEX  该管道是双向的,服务器和客户端进程都可以从管道读取或者向管道写入数据。
          //PIPE_ACCESS_INBOUND 该管道中数据是从客户端流向服务端,即客户端只能写,服务端只能读。
          //PIPE_ACCESS_OUTBOUND 该管道中数据是从服务端流向客户端,即客户端只能读,服务端只能写。
          DWORD dwPipeMode,//管道的模式
          //PIPE_TYPE_BYTE   数据作为一个连续的字节数据流写入管道。
          //PIPE_TYPE_MESSAGE 数据用数据块(名为“消息”或“报文”)的形式写入管道。
          //PIPE_READMODE_BYTE 数据以单独字节的形式从管道中读出。
          //PIPE_READMODE_MESSAGE 数据以名为“消息”的数据块形式从管道中读出(要求指定PIPE_TYPE_MESSAGE)。
          //PIPE_WAIT 同步操作在等待的时候挂起线程。
          //PIPE_NOWAIT 同步操作立即返回。
          DWORD nMaxInstances,//表示该管道所能够创建的最大实例数量。必须是1到常数PIPE_UNLIMITED_INSTANCES(255)间的一个值。
          DWORD nOutBufferSize,//表示管道的输出缓冲区容量,为0表示使用默认大小。
          DWORD nInBufferSize,//表示管道的输入缓冲区容量,为0表示使用默认大小。
          DWORD nDefaultTimeOut,//表示管道的默认等待超时。
          LPSECURITY_ATTRIBUTES lpSecurityAttributes//表示管道的安全属性。
        );

创建完成后等待连接

        BOOL WINAPI ConnectNamedPipe(
            HANDLE hNamedPipe,//命名管道句柄
            LPOVERLAPPED lpOverlapped//一般为NULL
        );

服务器端就绪后,客户端开始连接

        BOOL WINAPI WaitNamedPipe(
          LPCTSTR lpNamedPipeName,//命名管道名称
          DWORD nTimeOut//等待时长
        );

连接成功后,打开管道进行数据通信,使用CreateFile,ReadFile和WriteFile

具体的例子

A程序作为服务器,不断从B程序接收数据,并发送到C程序中:

#include "stdafx.h"
#include <stdio.h>
#include <conio.h>  
#include <tchar.h>
#include <Windows.h>
#include <process.h>
#include <stdlib.h>
LPCWSTR pStrPipeNameGet = L"\\\\.\\pipe\\Name_pipe_demon_get";
LPCWSTR pStrPipeNameSend = L"\\\\.\\pipe\\Name_pipe_demon_send";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];
DWORD dwLen;
HANDLE get, mSend, mutex;
LPCRITICAL_SECTION cs;

WCHAR* toWChar(const char *c){
    WCHAR wszClassName[256];
    memset(wszClassName, 0, sizeof(wszClassName));
    MultiByteToWideChar(CP_ACP, 0, c, strlen(c) + 1, wszClassName,
        sizeof(wszClassName) / sizeof(wszClassName[0]));
    return wszClassName;
}
void beginGetThread(PVOID p){
    printf("服务器Get\n");
    printf("等待连接......\n");

    HANDLE hPipe = CreateNamedPipe(pStrPipeNameGet, PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);
    if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待连接。  
    {
        printf("连接成功,开始接收数据\n");
        while (true)
        {
            WaitForSingleObject(mutex, INFINITE);
            EnterCriticalSection(cs);
            //接收客户端发送的数据  
            ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);
            printf("接收到来自A(程序B)的数据长度为%d字节\n", dwLen);
            printf("具体数据内容如下:");
            int bufSize;
            for (bufSize = 0; bufSize < (int)dwLen; bufSize++){
                putchar(buf[bufSize]);
            }
            LeaveCriticalSection(cs);
            Sleep(500);
            ReleaseSemaphore(mutex, 1, NULL);
            putchar('\n');
        }
    }
    else
    {
        printf("连接失败\n");
    }
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);//关闭管道
}

void beginSendThread(PVOID p){
    printf("服务器Send\n");
    printf("等待连接......\n");

    HANDLE hPipe = CreateNamedPipe(pStrPipeNameSend, PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);
    if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待连接。  
    {
        printf("连接成功,开始发送缓冲区数据至B(程序C)\n");
        //while (true)
        {
            WaitForSingleObject(mutex, INFINITE);
            EnterCriticalSection(cs);
            WriteFile(hPipe, buf, (int)dwLen, &dwLen, NULL);
            LeaveCriticalSection(cs);
            Sleep(500);
            ReleaseSemaphore(mutex, 1, NULL);
        }
    }
    else
    {
        printf("连接失败\n");
    }
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);//关闭管道
}
int _tmain(int argc, _TCHAR* argv[])
{
    cs = (LPCRITICAL_SECTION)malloc(sizeof(LPCRITICAL_SECTION));
    InitializeCriticalSection(cs);
    mutex = CreateSemaphore(NULL, 1, 1, TEXT("mutex"));
    _beginthread(beginGetThread, NULL, NULL);
    _beginthread(beginSendThread, NULL, NULL);
    Sleep(INFINITE);
    DeleteCriticalSection(cs);
    return 0;
}
B程序不断接收从键盘输入的数据,数据以回车结束,并发送给A:

#include "stdafx.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h>  
LPCWSTR pStrPipeName = L"\\\\.\\pipe\\Name_pipe_demon_get";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];

int _tmain(int argc, _TCHAR* argv[])
{
    printf("按任意键以开始连接Get\n");
    _getch();
    printf("A开始等待......\n");
    if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))
    {
        printf("Error! 连接Get失败\n");
        return 0;
    }
    HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_WRITE, 0,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    while (true)
    {
        printf("请输入要向服务端发送的数据,回车键结束,最大1024个字节\n");
        DWORD dwLen = 0;
        int bufSize;
        for (bufSize = 0; bufSize < BUFFER_MAX_LEN; bufSize++){
            buf[bufSize] = getchar();
            if (buf[bufSize] == '\n') break;
        }
        //向服务端发送数据  
        if (WriteFile(hPipe, buf, bufSize, &dwLen, NULL)){
            printf("数据写入完毕共%d字节\n", dwLen);
        }
        else
        {
            printf("数据写入失败\n");
        }
        Sleep(1000);
    }
    CloseHandle(hPipe);
    return 0;
}

C程序接收到从A发送来的数据,并转换成大写写入文件:

#include "stdafx.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h> 
LPCWSTR pStrPipeName = L"\\\\.\\pipe\\Name_pipe_demon_send";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];
DWORD dwLen = 0;

int _tmain(int argc, _TCHAR* argv[])
{
    printf("按任意键以开始连接Send\n");
    _getch();
    printf("B开始等待......\n");
    if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))
    {
        printf("Error! 连接Send失败\n");
        return 0;
    }
    HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ, 0,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    while (true)
    {
        // 接收服务端发回的数据
        ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);//读取管道中的内容(管道是一种特殊的文件)  
        printf("接收服务端发来的信息,长度为%d字节\n", dwLen);
        printf("具体数据内容如下:");
        HANDLE hWrite = CreateFile(_T("data.txt"), GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        for (int j = 0; j <= dwLen; j++){
            putchar(buf[j]);
            buf[j] = toupper(buf[j]);
        }
        putchar('\n');
        SetFilePointer(hWrite, NULL, NULL, FILE_END);
        WriteFile(hWrite, buf,  BUFFER_MAX_LEN, &dwLen, NULL);
        WriteFile(hWrite, "\n",  BUFFER_MAX_LEN, &dwLen, NULL);
        CloseHandle(hWrite);
        Sleep(1000);
    }
    CloseHandle(hPipe);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值