「命名管道」进程间通信——Console程序设计

    技术2022-05-19  27

    命名管道服务端

     

    //创建一个异步(重叠I/O)服务端,反射客户端发来的消息

     

    #pragma once

     

    #include <stdio.h>

    #include <tchar.h>

    #include <Windows.h>

     

    #define NUM_PIPES 5

    #define BUFFER_SIZE 256

     

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        HANDLE PipeHandles[NUM_PIPES];

        DWORD BytesTransferred;

        CHAR Buffer[NUM_PIPES][BUFFER_SIZE];

        INT i;

        OVERLAPPED Ovlap[NUM_PIPES];

        HANDLE Event[NUM_PIPES];

     

        BOOL DataRead[NUM_PIPES];

     

        DWORD Ret;

        DWORD Pipe;

     

        for(i = 0; i < NUM_PIPES; i++)

        {

            // 创建命名管道实例

            // PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED 双工异步模式

            // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE 字节流通信

            // NUM_PIPES 连接实例数

            // 默认缓冲区大小,等待超时时间1秒,默认安全符

            if ((PipeHandles[i] = CreateNamedPipe(TEXT(".//PIPE//Tc"),

                PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,

                PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, NUM_PIPES,

                0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)

            {

                printf("创建命名管道实例 %d 失败, 错误代码: %d/n",

                      i, GetLastError());

                return i;

            }

     

            // 创建无名的事件对象

            // 使用默认安全符,手工复位方式,初试状态为无信号,名称无

            if ((Event[i] = CreateEvent(NULL, TRUE, FALSE, NULL))

                == NULL)

            {

                printf("创建命名管道事件对象实例 %d 失败,错误代码: %d/n",

                    i, GetLastError());

                continue;

            }

     

            // 管道实例状态初始化

            DataRead[i] = FALSE;

     

            // 初始化用于异步操作的结构

            ZeroMemory(&Ovlap[i], sizeof(OVERLAPPED));

            Ovlap[i].hEvent = Event[i];

     

            // 启动监听

            // 因异步模式,该函数立刻返回,通过索检错误代码,判断是否成功

            if (ConnectNamedPipe(PipeHandles[i], &Ovlap[i]) == 0)

            {

                if (GetLastError() != ERROR_IO_PENDING)

                {

                    printf("启动命名管道实例 %d 监听模式失败,错误代码: %d/n",

                          i, GetLastError());

                    CloseHandle(PipeHandles[i]);

                    return i;

                }

            }

        }

     

        printf("命名管道服务开始运行/n");

     

        while(TRUE)

        {

            // 开始等候一个事件对象发生变化

            // NUM_PIPES 事件对象最大数

            // Event 事件对象句柄数组指针

            // FALSE 当其中一个信号量有效时就向下执行,为TRUE 则等待所有信号量有效再往下执行

            // INFINITE 等待时间为无限

            if ((Ret = WaitForMultipleObjects(NUM_PIPES, Event,

                FALSE, INFINITE)) == WAIT_FAILED)

            {

                printf("等待信号时发生错误,错误代码: %d/n",

                    GetLastError());

                return -1;

            }

            // 返回值减去WAIT_OBJECT_0 就是当前有效事件对象的数组序号

            // 如果同时有多个内核对象被触发,函数返回的只是其中序号最小的那个

            Pipe = Ret - WAIT_OBJECT_0;

     

            // 把指定的事件对象设置为无信号状态

            ResetEvent(Event[Pipe]);

     

            // 索检一个异步(重叠)操作实例当前的状态

            // PipeHandles[Pipe] 要索检的管道句柄

            // Ovlap[Pipe] 之前设定的该管道实例的异步结构

            // &BytesTransferred 用于记录该次传输字节数量的一个缓冲区

            // TRUE 指明一直等到异步操作结束才返回;FALSE表示立即返回

            if (GetOverlappedResult(PipeHandles[Pipe], &Ovlap[Pipe],

                &BytesTransferred, TRUE) == 0)

            {

                printf("取得异步操作状态失败或客户端关闭连接,错误代码: %d/n 重新启动该管道实例/n",

                    GetLastError());

     

                // 断开当前管道连接

                if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)

                {

                    printf("管道断开失败,错误代码: %d/n",

                        GetLastError());

                    return -1;

                }

     

                // 重新开始监听当前管道实例

                if (ConnectNamedPipe(PipeHandles[Pipe], &Ovlap[Pipe]) == 0)

                {

                    if (GetLastError() != ERROR_IO_PENDING)

                    {

                        printf("管道实例 %d 重启失败,错误代码: %d/n",

                              i, GetLastError());

                        // 关闭失败的管道实例

                        CloseHandle(PipeHandles[Pipe]);

                    }

                }

                // 管道实例状态

                DataRead[Pipe] = FALSE;

            }

            // 当取得一个重叠操作状态未发生错误时,则开始读或写操作

            else

            {

                if (DataRead[Pipe] == FALSE)

                {

                    // 重设当前异步结构

                    ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));

                    Ovlap[Pipe].hEvent = Event[Pipe];

     

                    // 读取数据,因异步操作,该函数立刻返回,当读取完毕时,触发事件对象有效

                    if (ReadFile(PipeHandles[Pipe], Buffer[Pipe],

                        BUFFER_SIZE, NULL, &Ovlap[Pipe]) == 0)

                    {

                        if (GetLastError() != ERROR_IO_PENDING)

                        {

                            printf("读取消息时发生错误,错误代码: %d/n",

                                GetLastError());

                        }

                    }

                    DataRead[Pipe] = TRUE;

                }

                else

                {

                    printf("读取到的数据大小为: %d bytes/n",

                        BytesTransferred);

     

                    ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));

                    Ovlap[Pipe].hEvent = Event[Pipe];

     

                    // 将读取到的数据返回给客户端

                    if (WriteFile(PipeHandles[Pipe], Buffer[Pipe],

                        BytesTransferred, NULL, &Ovlap[Pipe]) == 0)

                    {

                        if (GetLastError() != ERROR_IO_PENDING)

                        {

                            printf("发送消息时发生错误,错误代码: %d/n",

                                GetLastError());

                        }

                    }

                    DataRead[Pipe] = FALSE;

                }

            }

        }      

     

        return 0;

    }

     

     

    命名管道客户端

     

    #pragma once

     

    #include <stdio.h>

    #include <tchar.h>

    #include <windows.h>

     

    #define PIPE_NAME TEXT(".//Pipe//Tc")

    #define MyDATE    TEXT("测试。。。测试")

     

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        HANDLE PipeHandle;

        DWORD  BytesWritten;

     

        // 检查是否存在一个现成的命名管道实例

        // PIPE_NAME 试图与之建立连接的命名管道服务器名称

        // NMPWAIT_WAIT_FOREVER 指定客户机可以等待一个管道进程的时间;

           // 让它在管道上完成一个待决的ConnectNamedPipe操作

           // 该常数表示无限制

        if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)

        {

            printf("等待管道连接失败,错误代码: %d/n",

                GetLastError());

            return -1;

        }

     

        // 打开一个管道

        // PIPE_NAME 要打开的命名管道服务器名称

        // GENERIC_READ | GENERIC_WRITE 打开模式,可读可写。该模式需要与服务端兼容

        // 参数三 0 表示不共享;因为一次只能有一个客户机访问一个管道实例

        // (LPSECURITY_ATTRIBUTES) NULL 该参数应设为NULL,除非需要子进程继承客户机的句柄

        // OPEN_EXISTING 管道实例必须已经存在,函数会在管道不存在的情况下调用失败

        // FILE_ATTRIBUTE_NORMAL 默认属性

        // (HANDLE) NULL 它对命名管道无效,应设为NULL

        if ((PipeHandle = CreateFile(PIPE_NAME,

            GENERIC_READ | GENERIC_WRITE, 0,

            (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,

            FILE_ATTRIBUTE_NORMAL,

            (HANDLE) NULL)) == INVALID_HANDLE_VALUE)

        {

            printf("打开管道失败,错误代码: %d/n", GetLastError());

            return -1;

        }

     

        if (WriteFile(PipeHandle, MyDATE, sizeof(MyDATE), &BytesWritten,

            NULL) == 0)

        {

            printf("发送消息失败,错误代码: %d/n", GetLastError());

            CloseHandle(PipeHandle);

            return -1;

        }

     

        printf("发送消息数量为: %d bytes", BytesWritten);

     

        CloseHandle(PipeHandle);

     

        return 0;

    }


    最新回复(0)