C++ 命名管道 IPC

技术:IPC,RPC,Windows General
主题:Named Pipe,Inter-process Communication
概要:
命名管道是一种进程间单工或双工的通信机制。它可以在管道服务器和一个或多个管道客户端间进行。客户端可以位于本机或互联网上的远程计算机。
PIPE_ACCESS_INBOUND(呼入):
Client (GENERIC_WRITE) ---> Server (GENERIC_READ)
PIPE_ACCESS_OUTBOUND(外传):
Client (GENERIC_READ) <--- Server (GENERIC_WRITE)
PIPE_ACCESS_DUPLEX(双工):
Client (GENERIC_READ or GENERIC_WRITE, or both) <--> 
Server (GENERIC_READ and GENERIC_WRITE)
GENERIC_READ(普通读)
GENERIC_WRITE(普通写)
下面的代码示例演示了如何调用CreateNamedPipe来创建一个名称为"\\.\pipe\SamplePipe", 的管道。支持全双工连接。这样客户端和服务端都可以从管道中读写数据。自定义安全选项使得认证的用户才具有对管道的读写权限。当有客户端连接管道时,服务端尝试调用ReadFile从管道中读出客户端的消息,并通过调用WriteFile写入响应消息。
如何演示:
1.在VS2008中编译CppNamedPipeClient 和CppNamedPipeServer 两个工程,如果成功你会获得两个可执行文件CppNamedPipeClient.exe 和 CppNamedPipeServer.exe.
2.运行CppNamedPipeServer.exe。如果管道创建成功,程序会以命令行形式输出以下信息:
Server:
 The named pipe (\\.\pipe\SamplePipe) is created.
 Waiting for the client's connection...
3.运行CppNamedPipeClient.exe。如果客户端成功连接到命名管道会输出下列信息:
Client:
 The named pipe (\\.\pipe\SamplePipe) is connected.
 同时服务器端会输出下面的消息来指示有一个客户端连接到管道
 Server:
 Client is connected.
 4.接下来客户端会尝试写入消息到命名管道,程序输出:
 Client:
 Send 56 bytes to server: "Default request from client"
 当服务端从客户端读取消息后打印出:
 Server:
 Receive 56 bytes from client: "Default request from client"
 接下来,服务端写入一个回应消息到管道。
 Server:
 Send 58 bytes to client: "Default response from server"
 然后客户端收到回应消息输出:
 Client:
 Receive 58 bytes from server: "Default response from server"
 最后断开连接,关闭管道。
 主要代码逻辑:
 1.调用CreateNamedPipe创建一个命名管道,指明管道的名称,方向,传输模式,安全属性等
 
// Create the named pipe.
   hNamedPipe = CreateNamedPipe(
       FULL_PIPE_NAME,             // Pipe name.
       PIPE_ACCESS_DUPLEX,         // The pipe is duplex; both server and 
                                   // client processes can read from and 
                                   // write to the pipe
       PIPE_TYPE_MESSAGE |         // Message type pipe 
       PIPE_READMODE_MESSAGE |     // Message-read mode 
       PIPE_WAIT,                  // Blocking mode is enabled
       PIPE_UNLIMITED_INSTANCES,   // Max. instances
       BUFFER_SIZE,                // Output buffer size in bytes
       BUFFER_SIZE,                // Input buffer size in bytes
       NMPWAIT_USE_DEFAULT_WAIT,   // Time-out interval
       pSa                         // Security attributes
       );

在这个事例中管道支持全双工通信。安全属性允许认证用户具有读写管道权限,所有管理员组成员具有对管道的全部权限

//
   //   FUNCTION: CreatePipeSecurity(PSECURITY_ATTRIBUTES *)
   //
   //   PURPOSE: The CreatePipeSecurity function creates and initializes a new 
   //   SECURITY_ATTRIBUTES structure to allow Authenticated Users read and 
   //   write access to a pipe, and to allow the Administrators group full 
   //   access to the pipe.
   //
   //   PARAMETERS:
   //   * ppSa - output a pointer to a SECURITY_ATTRIBUTES structure that allows 
   //     Authenticated Users read and write access to a pipe, and allows the 
   //     Administrators group full access to the pipe. The structure must be 
   //     freed by calling FreePipeSecurity.
   //
   //   RETURN VALUE: Returns TRUE if the function succeeds..
   //
   //   EXAMPLE CALL:
   //
   //     PSECURITY_ATTRIBUTES pSa = NULL;
   //     if (CreatePipeSecurity(&pSa))
   //     {
   //         // Use the security attributes
   //         // ...
   //
   //         FreePipeSecurity(pSa);
   //     }
   //
   BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *ppSa)
 2.调用ConnectNamedPipe来等待客户端连接

 if (!ConnectNamedPipe(hNamedPipe, NULL))
   {
       if (ERROR_PIPE_CONNECTED != GetLastError())
       {
           dwError = GetLastError();
           wprintf(L"ConnectNamedPipe failed w/err 0x%08lx\n", dwError);
           goto Cleanup;
       }
   }
3.通过调用ReadFile和WriteFile从管道中读出客户端请求并写入响应数据
// 
   // Receive a request from client.
   // 

   BOOL fFinishRead = FALSE;
   do
   {
       wchar_t chRequest[BUFFER_SIZE];
       DWORD cbRequest, cbRead;
       cbRequest = sizeof(chRequest);

       fFinishRead = ReadFile(
           hNamedPipe,     // Handle of the pipe
           chRequest,      // Buffer to receive data
           cbRequest,      // Size of buffer in bytes
           &cbRead,        // Number of bytes read
           NULL            // Not overlapped I/O
           );

       if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
       {
           dwError = GetLastError();
           wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
           goto Cleanup;
       }

       wprintf(L"Receive %ld bytes from client: \"%s\"\n", cbRead, chRequest);

   } while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA

   // 
   // Send a response from server to client.
   // 

   wchar_t chResponse[] = RESPONSE_MESSAGE;
   DWORD cbResponse, cbWritten;
   cbResponse = sizeof(chResponse);

   if (!WriteFile(
       hNamedPipe,     // Handle of the pipe
       chResponse,     // Buffer to write
       cbResponse,     // Number of bytes to write 
       &cbWritten,     // Number of bytes written 
       NULL            // Not overlapped I/O
       ))
   {
       dwError = GetLastError();
       wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
       goto Cleanup;
   }

   wprintf(L"Send %ld bytes to client: \"%s\"\n", cbWritten, chResponse);
4.调用FlushFileBuffers在断开连接之前允许客户端读取管道内容。然后断开客户端连接

FlushFileBuffers(hNamedPipe);
DisconnectNamedPipe(hNamedPipe);
 5.关闭管道

CloseHandle(hNamedPipe);

完整代码:

CppNamedPipeServer.cpp

#pragma region Includes
#include <stdio.h>
#include <windows.h>
#include <sddl.h>
#pragma endregion


// The full name of the pipe in the format of \\servername\pipe\pipename.
#define SERVER_NAME         L"."
#define PIPE_NAME           L"SamplePipe"
#define FULL_PIPE_NAME      L"\\\\" SERVER_NAME L"\\pipe\\" PIPE_NAME

#define BUFFER_SIZE     1024

// Response message from client to server. '\0' is appended in the end 
// because the client may be a native C++ application that expects NULL 
// termiated string.
#define RESPONSE_MESSAGE    L"Default response from server"


BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *);
void FreePipeSecurity(PSECURITY_ATTRIBUTES);


int wmain(int argc, wchar_t* argv[])
{
    DWORD dwError = ERROR_SUCCESS;
    PSECURITY_ATTRIBUTES pSa = NULL;
    HANDLE hNamedPipe = INVALID_HANDLE_VALUE;

    // Prepare the security attributes (the lpSecurityAttributes parameter in 
    // CreateNamedPipe) for the pipe. This is optional. If the 
    // lpSecurityAttributes parameter of CreateNamedPipe is NULL, the named 
    // pipe gets a default security descriptor and the handle cannot be 
    // inherited. The ACLs in the default security descriptor of a pipe grant 
    // full control to the LocalSystem account, (elevated) administrators, 
    // and the creator owner. They also give only read access to members of 
    // the Everyone group and the anonymous account. However, if you want to 
    // customize the security permission of the pipe, (e.g. to allow 
    // Authenticated Users to read from and write to the pipe), you need to 
    // create a SECURITY_ATTRIBUTES structure.
    if (!CreatePipeSecurity(&pSa))
    {
        dwError = GetLastError();
        wprintf(L"CreatePipeSecurity failed w/err 0x%08lx\n", dwError);
        goto Cleanup;
    }

    // Create the named pipe.
    hNamedPipe = CreateNamedPipe(
        FULL_PIPE_NAME,             // Pipe name.
        PIPE_ACCESS_DUPLEX,         // The pipe is duplex; both server and 
                                    // client processes can read from and 
                                    // write to the pipe
        PIPE_TYPE_MESSAGE |         // Message type pipe 
        PIPE_READMODE_MESSAGE |     // Message-read mode 
        PIPE_WAIT,                  // Blocking mode is enabled
        PIPE_UNLIMITED_INSTANCES,   // Max. instances
        BUFFER_SIZE,                // Output buffer size in bytes
        BUFFER_SIZE,                // Input buffer size in bytes
        NMPWAIT_USE_DEFAULT_WAIT,   // Time-out interval
        pSa                         // Security attributes
        );

    if (hNamedPipe == INVALID_HANDLE_VALUE)
    {
        dwError = GetLastError();
        wprintf(L"Unable to create named pipe w/err 0x%08lx\n", dwError);
        goto Cleanup;
    }

    wprintf(L"The named pipe (%s) is created.\n", FULL_PIPE_NAME);

    // Wait for the client to connect.
    wprintf(L"Waiting for the client's connection...\n");
    if (!ConnectNamedPipe(hNamedPipe, NULL))
    {
        if (ERROR_PIPE_CONNECTED != GetLastError())
        {
            dwError = GetLastError();
            wprintf(L"ConnectNamedPipe failed w/err 0x%08lx\n", dwError);
            goto Cleanup;
        }
    }
    wprintf(L"Client is connected.\n");

    // 
    // Receive a request from client.
    // 

    BOOL fFinishRead = FALSE;
    do
    {
        wchar_t chRequest[BUFFER_SIZE];
        DWORD cbRequest, cbRead;
        cbRequest = sizeof(chRequest);

        fFinishRead = ReadFile(
            hNamedPipe,     // Handle of the pipe
            chRequest,      // Buffer to receive data
            cbRequest,      // Size of buffer in bytes
            &cbRead,        // Number of bytes read
            NULL            // Not overlapped I/O
            );

        if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
        {
            dwError = GetLastError();
            wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
            goto Cleanup;
        }

        wprintf(L"Receive %ld bytes from client: \"%s\"\n", cbRead, chRequest);

    } while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA

    // 
    // Send a response from server to client.
    // 

    wchar_t chResponse[] = RESPONSE_MESSAGE;
    DWORD cbResponse, cbWritten;
    cbResponse = sizeof(chResponse);

    if (!WriteFile(
        hNamedPipe,     // Handle of the pipe
        chResponse,     // Buffer to write
        cbResponse,     // Number of bytes to write 
        &cbWritten,     // Number of bytes written 
        NULL            // Not overlapped I/O
        ))
    {
        dwError = GetLastError();
        wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
        goto Cleanup;
    }

    wprintf(L"Send %ld bytes to client: \"%s\"\n", cbWritten, chResponse);

    // Flush the pipe to allow the client to read the pipe's contents 
    // before disconnecting. Then disconnect the client's connection. 
    FlushFileBuffers(hNamedPipe);
    DisconnectNamedPipe(hNamedPipe);

Cleanup:

    // Centralized cleanup for all allocated resources.
    if (pSa != NULL)
    {
        FreePipeSecurity(pSa);
        pSa = NULL;
    }
    if (hNamedPipe != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hNamedPipe);
        hNamedPipe = INVALID_HANDLE_VALUE;
    }

    return dwError;
}


//
//   FUNCTION: CreatePipeSecurity(PSECURITY_ATTRIBUTES *)
//
//   PURPOSE: The CreatePipeSecurity function creates and initializes a new 
//   SECURITY_ATTRIBUTES structure to allow Authenticated Users read and 
//   write access to a pipe, and to allow the Administrators group full 
//   access to the pipe.
//
//   PARAMETERS:
//   * ppSa - output a pointer to a SECURITY_ATTRIBUTES structure that allows 
//     Authenticated Users read and write access to a pipe, and allows the 
//     Administrators group full access to the pipe. The structure must be 
//     freed by calling FreePipeSecurity.
//
//   RETURN VALUE: Returns TRUE if the function succeeds.
//
//   EXAMPLE CALL:
//
//     PSECURITY_ATTRIBUTES pSa = NULL;
//     if (CreatePipeSecurity(&pSa))
//     {
//         // Use the security attributes
//         // ...
//
//         FreePipeSecurity(pSa);
//     }
//
BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *ppSa)
{
    BOOL fSucceeded = TRUE;
    DWORD dwError = ERROR_SUCCESS;

    PSECURITY_DESCRIPTOR pSd = NULL;
    PSECURITY_ATTRIBUTES pSa = NULL;

    // Define the SDDL for the security descriptor.
    PCWSTR szSDDL = L"D:"       // Discretionary ACL
        L"(A;OICI;GRGW;;;AU)"   // Allow read/write to authenticated users
        L"(A;OICI;GA;;;BA)";    // Allow full control to administrators

    if (!ConvertStringSecurityDescriptorToSecurityDescriptor(szSDDL, 
        SDDL_REVISION_1, &pSd, NULL))
    {
        fSucceeded = FALSE;
        dwError = GetLastError();
        goto Cleanup;
    }
    
    // Allocate the memory of SECURITY_ATTRIBUTES.
    pSa = (PSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(*pSa));
    if (pSa == NULL)
    {
        fSucceeded = FALSE;
        dwError = GetLastError();
        goto Cleanup;
    }

    pSa->nLength = sizeof(*pSa);
    pSa->lpSecurityDescriptor = pSd;
    pSa->bInheritHandle = FALSE;

    *ppSa = pSa;

Cleanup:
    // Clean up the allocated resources if something is wrong.
    if (!fSucceeded)
    {
        if (pSd)
        {
            LocalFree(pSd);
            pSd = NULL;
        }
        if (pSa)
        {
            LocalFree(pSa);
            pSa = NULL;
        }

        SetLastError(dwError);
    }
    
    return fSucceeded;
}


//
//   FUNCTION: FreePipeSecurity(PSECURITY_ATTRIBUTES)
//
//   PURPOSE: The FreePipeSecurity function frees a SECURITY_ATTRIBUTES 
//   structure that was created by the CreatePipeSecurity function. 
//
//   PARAMETERS:
//   * pSa - pointer to a SECURITY_ATTRIBUTES structure that was created by 
//     the CreatePipeSecurity function. 
//
void FreePipeSecurity(PSECURITY_ATTRIBUTES pSa)
{
    if (pSa)
    {
        if (pSa->lpSecurityDescriptor)
        {
            LocalFree(pSa->lpSecurityDescriptor);
        }
        LocalFree(pSa);
    }
}
CppNamedPipeClient.cpp

#pragma region Includes
#include <stdio.h>
#include <windows.h>
#pragma endregion


// The full name of the pipe in the format of \\servername\pipe\pipename.
#define SERVER_NAME         L"."
#define PIPE_NAME           L"SamplePipe"
#define FULL_PIPE_NAME      L"\\\\" SERVER_NAME L"\\pipe\\" PIPE_NAME

#define BUFFER_SIZE     1024

// Request message from client to server.
#define REQUEST_MESSAGE     L"Default request from client"


int wmain(int argc, wchar_t* argv[])
{
    HANDLE hPipe = INVALID_HANDLE_VALUE;
    DWORD dwError = ERROR_SUCCESS;

    // Try to open the named pipe identified by the pipe name.
    while (TRUE) 
    {
        hPipe = CreateFile( 
            FULL_PIPE_NAME,                 // Pipe name 
            GENERIC_READ | GENERIC_WRITE,   // Read and write access
            0,                              // No sharing 
            NULL,                           // Default security attributes
            OPEN_EXISTING,                  // Opens existing pipe
            0,                              // Default attributes
            NULL                            // No template file
            );

        // If the pipe handle is opened successfully ...
        if (hPipe != INVALID_HANDLE_VALUE)
        {
            wprintf(L"The named pipe (%s) is connected.\n", FULL_PIPE_NAME);
            break;
        }

        dwError = GetLastError();

        // Exit if an error other than ERROR_PIPE_BUSY occurs.
        if (ERROR_PIPE_BUSY != dwError)
        {
            wprintf(L"Unable to open named pipe w/err 0x%08lx\n", dwError);
            goto Cleanup;
        }

        // All pipe instances are busy, so wait for 5 seconds.
        if (!WaitNamedPipe(FULL_PIPE_NAME, 5000))
        {
            dwError = GetLastError();
            wprintf(L"Could not open pipe: 5 second wait timed out.");
            goto Cleanup;
        }
    }

    // Set the read mode and the blocking mode of the named pipe. In this 
    // sample, we set data to be read from the pipe as a stream of messages.
    DWORD dwMode = PIPE_READMODE_MESSAGE;
    if (!SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL))
    {
        dwError = GetLastError();
        wprintf(L"SetNamedPipeHandleState failed w/err 0x%08lx\n", dwError);
        goto Cleanup;
    }

    // 
    // Send a request from client to server
    // 

    wchar_t chRequest[] = REQUEST_MESSAGE;
    DWORD cbRequest, cbWritten;
    cbRequest = sizeof(chRequest);

    if (!WriteFile(
        hPipe,                      // Handle of the pipe
        chRequest,                  // Message to be written
        cbRequest,                  // Number of bytes to write
        &cbWritten,                 // Number of bytes written
        NULL                        // Not overlapped
        ))
    {
        dwError = GetLastError();
        wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
        goto Cleanup;
    }

    wprintf(L"Send %ld bytes to server: \"%s\"\n", cbWritten, chRequest);

    //
    // Receive a response from server.
    // 

    BOOL fFinishRead = FALSE;
    do
    {
        wchar_t chResponse[BUFFER_SIZE];
        DWORD cbResponse, cbRead;
        cbResponse = sizeof(chResponse);

        fFinishRead = ReadFile(
            hPipe,                  // Handle of the pipe
            chResponse,             // Buffer to receive the reply
            cbResponse,             // Size of buffer in bytes
            &cbRead,                // Number of bytes read 
            NULL                    // Not overlapped 
            );

        if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
        {
            dwError = GetLastError();
            wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
            goto Cleanup;
        }

        wprintf(L"Receive %ld bytes from server: \"%s\"\n", cbRead, chResponse);

    } while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA

Cleanup:

    // Centralized cleanup for all allocated resources.
    if (hPipe != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hPipe);
        hPipe = INVALID_HANDLE_VALUE;
    }

    return dwError;
}






 
 
 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows 中,可以使用命名管道(Named Pipes)实现进程间通信。以下是一些基本步骤: 1. 创建命名管道 使用 CreateNamedPipe 函数创建一个命名管道。该函数需要指定管道名称、管道的读写模式、管道的最大实例数等参数。 2. 等待客户端连接 使用 ConnectNamedPipe 函数等待客户端的连接。该函数会一直阻塞,直到有客户端连接成功。 3. 接收客户端数据 使用 ReadFile 函数从管道中读取客户端发送的数据。 4. 发送数据给客户端 使用 WriteFile 函数向管道中写入数据,以便客户端读取。 5. 断开连接 使用 DisconnectNamedPipe 函数断开与客户端的连接。如果需要与多个客户端通信,则返回第 2 步。 6. 关闭管道 使用 CloseHandle 函数关闭命名管道的句柄。 注意事项: - 在创建管道时,需要指定管道名称,该名称在系统中必须是唯一的。 - 管道支持同步和异步方式进行读写操作,可以根据具体需求选择使用哪种方式。 - 管道的读写操作是阻塞式的,也可以使用 overlapped 结构体实现异步操作。 下面是一个简单的代码示例,演示如何使用命名管道实现进程间通信: ``` #include <windows.h> #include <stdio.h> #define PIPE_NAME "\\\\.\\pipe\\MyPipe" int main() { HANDLE hPipe; char buffer[1024]; DWORD dwRead; // 创建命名管道 hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 0, NULL); if (hPipe == INVALID_HANDLE_VALUE) { printf("CreateNamedPipe failed! Error code: %d\n", GetLastError()); return 1; } // 等待客户端连接 if (!ConnectNamedPipe(hPipe, NULL)) { printf("ConnectNamedPipe failed! Error code: %d\n", GetLastError()); return 1; } // 接收客户端数据 if (!ReadFile(hPipe, buffer, sizeof(buffer), &dwRead, NULL)) { printf("ReadFile failed! Error code: %d\n", GetLastError()); return 1; } printf("Received data from client: %s\n", buffer); // 发送数据给客户端 if (!WriteFile(hPipe, "Hello, client!", 15, NULL, NULL)) { printf("WriteFile failed! Error code: %d\n", GetLastError()); return 1; } // 断开连接 DisconnectNamedPipe(hPipe); // 关闭管道 CloseHandle(hPipe); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值