Windows进程间通信(IPC)编程

一、文件映射

        文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。

参考连接:https://blog.csdn.net/qq_20183489/article/details/54646794

        另外,内存映射文件在处理大数据量的文件时表现出了良好的性能(实际上,文件越大,内存映射的优势就越明显)。

参考连接:https://blog.csdn.net/zzq060143/article/details/54619571

二、共享内存

        Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF(INVALID_HANDLE_VALUE)来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。

共享内存实现数据共享示例如下:

//发送数据的进程先启动,用于发送数据,即将数据写入视图 。

#include "stdafx.h"
#include <Windows.h>
#include <conio.h>

#define BUFFER_SIZE    256
TCHAR szMapFileName[] = TEXT("MyFileMappingName");  //映射文件名,即共享内存的名称
TCHAR szSendData[]    = TEXT("Message from the send process.");

int main()
{
    HANDLE  hMapFile = NULL;
    LPCTSTR pBuf = NULL;

    //1. 创建一个文件映射内核对象
    hMapFile = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUFFER_SIZE, szMapFileName);  //INVALID_HANDLE_VALUE表示创建一个进程间共享的对象
    if (NULL == hMapFile)
    {
        _tprintf(TEXT("Could not create file mapping object (%d).\n"), GetLastError());
        return -1;
    }

    //2. 将文件数据映射到进程的地址空间
    pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE);
    if (NULL == pBuf)
    {
        _tprintf(TEXT("Could not map view of file (%d). \n"), GetLastError());

        CloseHandle(hMapFile);
        hMapFile = NULL;

        return -1;
    }

    //3. 写入到内存中
    CopyMemory((void*)pBuf, szSendData, _tcslen(szSendData) * sizeof(TCHAR));
    _getch();  //这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车

    //4. 从进程的地址空间中撤消文件数据的映像
    UnmapViewOfFile(pBuf);

    //5. 关闭文件映射对象和文件对象
    CloseHandle(hMapFile);

    getchar();
    return 0;
}
//接收数据的进程后启动,用于接收数据,即读取视图的数据

#include "stdafx.h"
#include <Windows.h>

#define BUFFER_SIZE    256
TCHAR szMapFileName[] = TEXT("MyFileMappingName");

int main()
{
    HANDLE  hMapFile = NULL;
    LPCTSTR pBuf = NULL;

    //1. 打开一个命名的文件映射内核对象
    hMapFile = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szMapFileName);
    if (NULL == hMapFile)
    {
        _tprintf(TEXT("Could not open file mapping object (%d).\n"), GetLastError());
        return -1;
    }

    //2. 将文件映射内核对象hFileMapping映射到当前应用程序的进程地址pBuf,通过该指针可以读写共享的内存区域
    pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE);
    if (NULL == pBuf)
    {
        _tprintf(TEXT("Could not map view of file (%d). \n"), GetLastError());

        CloseHandle(hMapFile);
        hMapFile = NULL;

        return -1;
    }

    //3. 显示接收到的数据
    for (int i = 0; i < _tcsclen(pBuf); i++)
    {
        _tprintf(TEXT("%c"), *(pBuf + i));
    }
    printf("\n");

    //4. 从进程的地址空间中撤消文件数据的映像
    UnmapViewOfFile(pBuf);

    //5. 关闭文件映射对象和文件对象
    CloseHandle(hMapFile);

    getchar();
    return 0;
}

 三、管道

        管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的-管道的两端点既可读也可写。

         匿名管道

        匿名管道(Anonymous Pipe)是在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。

        匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。

匿名管道通信过程:

    >>父进程读写过程

      ①创建匿名管道

      ②创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取/写入句柄赋值给子进程的输入/输出句柄)。

      ③关闭子进程相关句柄。(进程句柄,主线程句柄)

      ④对管道读写

    >>子进程读写过程

      ①获得输入输出句柄

      ②对管道读写

相关函数:CreatePipe()创建管道

函数原型:

BOOL CreatePipe(
PHANDLE hReadPipe,   // pointer to read handle
PHANDLE hWritePipe,  // pointer to write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes,  // pointer to security attributes
DWORD nSize // pipe size
);

参数说明:

hReadPipe    作为返回类型使用,返回管道读取句柄

hWritePipe    作为返回类型使用,返回管道写入句柄

lpPipeAttributes  指向SECURITY_ATTRIBUTES结构体的指针,检测返回句柄是否能被子进程继承,如果此参数为NULL,则句柄不能被继承

nSize        指定管道的缓冲区大小,改大小只是个建议值,系统将用这个值来计算一个适当的缓存区大小,如果此参数是0,系统会使用默认的缓冲区大小

返回值:                  若函数成功返回非零值,若函数失败返回0,详细消息可以调用GetLastError函数获得

 代码实例:

// 父进程.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    const int nBufferLen = 256;

    SECURITY_ATTRIBUTES sa;
    HANDLE hRead = NULL;
    HANDLE hWrite = NULL;

    STARTUPINFO sui;
    PROCESS_INFORMATION pi;

    char  szBuffer[nBufferLen] = { 0 };
    DWORD dwReadLen = 0;

    BOOL bRet = FALSE;

    //1. 创建匿名管道
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    bRet = ::CreatePipe(&hRead, &hWrite, &sa, 0);
    if (!bRet)
    {
        cout << "创建匿名管道失败!" << endl;
        system("pause");
        return -1;
    }

    //2. 创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取写入句柄赋予子进程的输入输出句柄)
    ZeroMemory(&sui, sizeof(STARTUPINFO));
    sui.cb = sizeof(STARTUPINFO);
    sui.dwFlags = STARTF_USESTDHANDLES;
    sui.hStdInput  = hRead;
    sui.hStdOutput = hWrite;
    sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    bRet = ::CreateProcess(L"..\\x64\\Debug\\子进程.exe", NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &sui, &pi);
    if (!bRet)
    {
        cout << "创建子进程失败!" << endl;
        system("pause");
        return -1;
    }

    //3. 关闭子进程相关句柄(进行句柄,进程主线程句柄)
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    Sleep(2000);

    //4. 读取数据
    bRet = ::ReadFile(hRead, szBuffer, nBufferLen, &dwReadLen, NULL);
    if (!bRet)
    {
        cout << "读取数据失败!" << endl;
        system("pause");
        return -1;
    }

    cout << "从子进程接收到到数据: " << szBuffer << endl;

    system("pause");
    return 0;
}

// 子进程.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    HANDLE hRead  = NULL;
    HANDLE hWrite = NULL;

    BOOL bRet = FALSE;

    //1. 获得匿名管道输入输出句柄
    hRead  = GetStdHandle(STD_INPUT_HANDLE);
    hWrite = GetStdHandle(STD_OUTPUT_HANDLE);

    char  szSendBuffer[] = "子进程写入管道成功!";
    DWORD dwWriteLen = 0;

    //2. 写入数据
    bRet = WriteFile(hWrite, szSendBuffer, (DWORD)strlen(szSendBuffer), &dwWriteLen, NULL);
    if (!bRet)
    {
        system("pause");
        return -1;
    }

    Sleep(500);

    system("pause");
    return 0;
}

        命名(有名)管道

        命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

        命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。

命名管道有两种通信方式:

        A. 字节模式:在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动

        B. 消息模式:在消息模式下,客户机和服务器则通过一系列不连续的数据单位,进行数据收发,每次在管道上发一条消息后,它必须作为一条完整的消息读入

        通信流程:

                服务器端: 创建命名管道 -> 服务器等待用户连接 -> 读写数据

                客户端:连接命名管道 -> 打开命名管道 -> 读写数据

相关函数:CreatePipe()创建管道

函数原型:

HANDLE CreateNamedPipe(
  LPCTSTR lpName, // pipe name
  DWORD dwOpenMode, // pipe open mode
  DWORD dwPipeMode, // pipe-specific modes
  DWORD nMaxInstances, // maximum number of instances
  DWORD nOutBufferSize, // output buffer size
  DWORD nInBufferSize, // input buffer size
  DWORD nDefaultTimeOut, // time-out interval
  LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
);

参数说明:

代码实例:

// 服务端.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    HANDLE hPipe  = NULL;
    HANDLE hEvent = NULL;
    DWORD  dwReadLen  = 0;
    DWORD  dwWriteLen = 0;
    OVERLAPPED ovlap;
    char senbuf[] = "This is server!";
    char rebuf[100];

    //1. 创建命名管道
    hPipe = CreateNamedPipe(L"\\\\.\\pipe\\Communication", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL);
    if (INVALID_HANDLE_VALUE == hPipe)
    {
        cout << "创建命名管道失败!" << endl;
        hPipe = NULL;
        system("pause");
        return -1;
    }

    hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == hEvent)
    {
        cout << "创建事件对象失败!" << endl;
        CloseHandle(hPipe);
        hPipe = NULL;
        system("pause");
        return -1;
    }

    ZeroMemory(&ovlap, sizeof(OVERLAPPED));
    ovlap.hEvent = hEvent;

    //2. 创建管道连接
    if (!ConnectNamedPipe(hPipe, &ovlap))
    {
        if (ERROR_IO_PENDING != GetLastError())
        {
            cout << "等待客户端连接失败!" << endl;
            CloseHandle(hPipe);
            CloseHandle(hEvent);
            hPipe = NULL;
            system("pause");
            return -1;
        }
    }

    //3. 等待客户端连接
    if ( WAIT_FAILED == WaitForSingleObject(hEvent, INFINITE))
    {
        cout << "等待对象失败!" << endl;
        CloseHandle(hPipe);
        CloseHandle(hEvent);
        hPipe = NULL;
        system("pause");
        return -1;
    }
    CloseHandle(hEvent);

    //4. 读写管道数据
    //4.1 读取数据
    if (!ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL))
    {
        cout << "读取数据失败!" << endl;
        system("pause");
        return -1;
    }
    cout << rebuf << endl;

    //4.2 写入数据
    if (!WriteFile(hPipe, senbuf, (DWORD)strlen(senbuf) + 1, &dwWriteLen, NULL))
    {
        cout << "写入数据失败!" << endl;
        system("pause");
        return -1;
    }

    system("pause");
    return 0;
}
// 客户端.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    HANDLE hPipe = NULL;
    HANDLE hEvent = NULL;
    DWORD  dwReadLen = 0;
    DWORD  dwWriteLen = 0;
    char senbuf[] = "This is client!";
    char rebuf[100];

    //1. 连接命名管道
    if (!WaitNamedPipe(L"\\\\.\\pipe\\Communication", NMPWAIT_WAIT_FOREVER))
    {
        cout << "当前没有可利用的命名管道实例!" << endl;
        system("pause");
        return -1;
    }

    //2. 打开命名管道
    hPipe = CreateFile(L"\\\\.\\pipe\\Communication", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hPipe)
    {
        cout << "打开命名管道失败!" << endl;
        hPipe = NULL;
        system("pause");
        return -1;
    }

    //3. 读写管道数据
    //3.1 写入数据
    if (!WriteFile(hPipe, senbuf, strlen(senbuf) + 1, &dwWriteLen, NULL))
    {
        cout << "写入数据失败!" << endl;
        system("pause");
        return -1;
    }

    //3.2 读取数据
    if (!ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL))
    {
        cout << "读取数据失败!" << endl;
        system("pause");
        return -1;
    }
    cout << rebuf << endl;

    system("pause");
    return 0;
}

正在更新中。。。

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值