【面经笔记】管道

进程间通信——匿名管道

匿名管道只能实现本机机器上两个进程的通信,通常用来在父子进程间通信,不能实现跨网络的通信。

参考孙鑫的深入解析VC++ 第17章,进程间通信:匿名管道

下面只贴出核心代码:


父进程:

void CParentView::OnPipeCreate()
{
    SECURITY_ATTRIBUTES sa; //安全结构体
    sa.bInheritHandle = TRUE;//子进程可以继承父进程的匿名管道读写句柄
    sa.lpSecurityDescriptor = NULL;//系统为创建的匿名管道赋予默认的安全描述符
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);//结构体大小,长度成员
    if (! CreatePipe(&hRead,&hWrite,&sa,0))
    {
        MessageBox(_T("管道创建失败!"));
        return;
    }
    STARTUPINFO sui;//创建新进程所需信息结构体
    PROCESS_INFORMATION pi;//进程信息结构体
    ZeroMemory(&sui, sizeof(STARTUPINFO));//初始化
    sui.cb = sizeof(STARTUPINFO);
    sui.dwFlags = STARTF_USESTDHANDLES;

    sui.hStdInput = hRead;//将子进程的标准输入设为管道
    sui.hStdOutput = hWrite;//将子进程的标准输出设为管道

    sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);

    auto tmp = CreateProcess(_T(".\\Child.exe"), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &sui, &pi);
    auto error = GetLastError();
    //创建子进程:允许子进程继承父进程句柄
    if (!CreateProcess(_T(".\\Child.exe"),NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
    {
        CloseHandle(hRead);
        CloseHandle(hWrite);
        hRead = NULL;
        hWrite = NULL;
        MessageBox(_T("创建子进程失败!"));
        return;
    }
    else
    {
        //关闭父进程对子进程的引用
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}
void CParentView::OnPipeRead()
{
    char buf[100];
    DWORD dwRread;//保存实际读取的字节数
    if (!ReadFile(hRead,buf,100,&dwRread,NULL))//从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,
    {
        MessageBox(_T("读取数据失败!"));
        return;
    }
    MessageBox((LPCTSTR)(LPTSTR)buf);
}
void CParentView::OnWrite()
{
    char buf[] = "首次管道通信成功!";
    DWORD dwWrite;//保存实际写入的字节数
    if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
    {
        MessageBox(_T("写入数据失败!"));
        return;
    }
    // TODO:  在此添加命令处理程序代码
}

子进程:

void CChildView::OnInitialUpdate()
{
    CView::OnInitialUpdate();

    hRead = GetStdHandle(STD_INPUT_HANDLE); //获取管道句柄!
    hWrite = GetStdHandle(STD_OUTPUT_HANDLE);   
}
void CChildView::OnPipeRead()
{
    char buf[100];
    DWORD dwRead;
    if (!ReadFile(hRead,buf,100,&dwRead,NULL))
    {
        MessageBox(_T("读取数据失败!"));
        return;
    }
    MessageBox((LPCTSTR)(LPTSTR)buf);
}

void CChildView::OnPipeWrite()
{
    char buf[] = "匿名管道测试成功!";
    DWORD dwWrite;
    if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
    {
        MessageBox(_T("写入数据失败!"));
        return;
    }
}

匿名管道没有名称,所以只能在父进程调用CreateProcess()函数创建子进程时,将管道的句柄传递给子进程。

父进程通过:

sui.hStdInput = hRead;//将子进程的标准输入设为管道
sui.hStdOutput = hWrite;//将子进程的标准输出设为管道

将匿名管道的句柄作为参数(sui)传递给子进程

子进程通过:

hRead = GetStdHandle(STD_INPUT_HANDLE); 
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);

获取匿名管道的句柄

利用管道,通过标准的Win32文件系统函数:readfile()、 writefile()进行数据的收发。


进程间通信——有名管道

  • 具体过程:

在服务器端调用CreateNamedPipe()创建命名管道后,调用ConnectNamedPipe()函数让服务器等待客户端连接,
在客户端,首先调用WaitNamedPipe()函数判断当前是否有可用的管道实例,如果有,则调用CreateFile()函数打开命名管道实例,建立连接。

CreateFile()函数:创建或打开文件 、 pipes对象,并返回一个可以用来访问这些对象的句柄。


服务的创建命名管道:

void CParentView::OnPipeCreate()
{
    //创建命名管道:双向模式、允许重叠方式
    //最大实例数值指同一个命名管道最多能创建的实例个数,如果希望同时接受5个客户端请求,需要调用createnamedpipe5次,每个实例某一时刻只能和一个客户端通信
    hpipe = CreateNamedPipe(_T("\\\\.\\pipe\\Mypipe"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL);
    if (INVALID_HANDLE_VALUE == hpipe)
    {
        MessageBox(_T("创建命名管道失败!"));
        hpipe = NULL;
        return;
    }
    HANDLE hEvent;//匿名人工重置事件对象
    hEvent = CreateEvent(NULL, TRUE, false, NULL);
    if (!hEvent)
    {
        MessageBox(_T("创建事件对象失败!"));
        CloseHandle(hpipe);
        hpipe = NULL;
        return;
    }
    OVERLAPPED ovlap;
    ZeroMemory(&ovlap, sizeof(OVERLAPPED));
    ovlap.hEvent = hEvent;

    if (!ConnectNamedPipe(hpipe,&ovlap))
    {
        if (ERROR_IO_PENDING != GetLastError())
        {
            MessageBox(_T("等待客户端连接失败!"));
            CloseHandle(hpipe);
            CloseHandle(hEvent);
            hpipe = NULL;
            return;
        }
    }
    if (WAIT_FAILED == WaitForSingleObject(hEvent,INFINITE))
    {
        MessageBox(_T("等待对象失败!"));
        CloseHandle(hpipe);
        CloseHandle(hEvent);
        hpipe = NULL;
        return;
    }
    CloseHandle(hEvent);
}

客户端连接命名管道:

void CChildView::OnPipeConnect()
{
    if (!WaitNamedPipe(_T("\\\\.\\pipe\\Mypipe"), NMPWAIT_WAIT_FOREVER))
    {
        MessageBox(_T("当前没有可利用的命名管道实例!"));
        return;
    }
    hpipe = CreateFile(_T("\\\\.\\pipe\\Mypipe"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hpipe)
    {
        MessageBox(_T("打开命名管道失败!"));
        hpipe = NULL;
        return;
    }
        // TODO:  在此添加命令处理程序代码
}

读写使用,两者是一样的:

void CParentView::OnPipeRead()
{
    char buf[100];
    DWORD dwRread;//保存实际读取的字节数
    if (!ReadFile(hpipe, buf, 100, &dwRread, NULL))//从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,
    {
        MessageBox(_T("读取数据失败!"));
        return;
    }
    MessageBox((LPCTSTR)(LPTSTR)buf);
}


void CParentView::OnWrite()
{
    char buf[] = "管道通信成功!";
    DWORD dwWrite;//保存实际写入的字节数
    if (!WriteFile(hpipe, buf, strlen(buf) + 1, &dwWrite, NULL))
    {
        MessageBox(_T("写入数据失败!"));
        return;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值