使用异步命名管道通信的实例

news2024/9/25 13:17:06

记录一个使用异步命名管道通信的实例。代码参考了 MSDN 的文档:使用完成例程的命名管道服务器 - Win32 apps | Microsoft Learn。

服务端代码

#include <windows.h> 
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>

#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
#define MAX_USERNAME_LEN 100
#define MAX_PASSWORD_LEN 100

typedef struct
{
    OVERLAPPED oOverlap;
    HANDLE hPipeInst;
    struct Message {
        TCHAR username[MAX_USERNAME_LEN];
        TCHAR password[MAX_PASSWORD_LEN];
        TCHAR request[BUFSIZE];
    } message;
    DWORD cbRead;
    TCHAR chReply[BUFSIZE];
    DWORD cbToWrite;
} PIPEINST, * LPPIPEINST;

VOID DisconnectAndClose(LPPIPEINST);
BOOL CreateAndConnectInstance(LPOVERLAPPED);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);

VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);

HANDLE hPipe;

int _tmain(VOID)
{
    HANDLE hConnectEvent;
    OVERLAPPED oConnect;
    LPPIPEINST lpPipeInst;
    DWORD dwWait, cbRet;
    BOOL fSuccess, fPendingIO;

    // Create one event object for the connect operation. 

    hConnectEvent = CreateEvent(
        NULL,    // default security attribute
        TRUE,    // manual reset event 
        TRUE,    // initial state = signaled 
        NULL);   // unnamed event object 

    if (hConnectEvent == NULL)
    {
        printf("CreateEvent failed with %d.\n", GetLastError());
        return 0;
    }

    oConnect.hEvent = hConnectEvent;

    // Call a subroutine to create one instance, and wait for 
    // the client to connect. 

    fPendingIO = CreateAndConnectInstance(&oConnect);

    while (1)
    {
        // Wait for a client to connect, or for a read or write 
        // operation to be completed, which causes a completion 
        // routine to be queued for execution. 

        dwWait = WaitForSingleObjectEx(
            hConnectEvent,  // event object to wait for 
            INFINITE,       // waits indefinitely 
            TRUE);          // alertable wait enabled 

        switch (dwWait)
        {
            // The wait conditions are satisfied by a completed connect 
            // operation. 
        case 0:
            // If an operation is pending, get the result of the 
            // connect operation. 

            if (fPendingIO)
            {
                fSuccess = GetOverlappedResult(
                    hPipe,     // pipe handle 
                    &oConnect, // OVERLAPPED structure 
                    &cbRet,    // bytes transferred 
                    FALSE);    // does not wait 
                if (!fSuccess)
                {
                    printf("ConnectNamedPipe (%d)\n", GetLastError());
                    return 0;
                }
            }

            // Allocate storage for this instance. 

            lpPipeInst = (LPPIPEINST)GlobalAlloc(
                GPTR, sizeof(PIPEINST));
            if (lpPipeInst == NULL)
            {
                printf("GlobalAlloc failed (%d)\n", GetLastError());
                return 0;
            }

            lpPipeInst->hPipeInst = hPipe;

            // Start the read operation for this client. 
            // Note that this same routine is later used as a 
            // completion routine after a write operation. 

            lpPipeInst->cbToWrite = 0;
            CompletedWriteRoutine(0, 0, (LPOVERLAPPED)lpPipeInst);

            // Create new pipe instance for the next client. 

            fPendingIO = CreateAndConnectInstance(
                &oConnect);
            break;

            // The wait is satisfied by a completed read or write 
            // operation. This allows the system to execute the 
            // completion routine. 

        case WAIT_IO_COMPLETION:
            break;

            // An error occurred in the wait function. 

        default:
        {
            printf("WaitForSingleObjectEx (%d)\n", GetLastError());
            return 0;
        }
        }
    }
    return 0;
}

// CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED) 
// This routine is called as a completion routine after writing to 
// the pipe, or when a new client has connected to a pipe instance.
// It starts another read operation. 

VOID WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
    LPOVERLAPPED lpOverLap)
{
    LPPIPEINST lpPipeInst;
    BOOL fRead = FALSE;

    // lpOverlap points to storage for this instance. 

    lpPipeInst = (LPPIPEINST)lpOverLap;

    // The write operation has finished, so read the next request (if 
    // there is no error). 

    if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
        fRead = ReadFileEx(
            lpPipeInst->hPipeInst,
            &lpPipeInst->message,
            sizeof(lpPipeInst->message),
            (LPOVERLAPPED)lpPipeInst,
            (LPOVERLAPPED_COMPLETION_ROUTINE)CompletedReadRoutine);

    // Disconnect if an error occurred. 

    if (!fRead)
        DisconnectAndClose(lpPipeInst);
}

// CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED) 
// This routine is called as an I/O completion routine after reading 
// a request from the client. It gets data and writes it to the pipe. 

VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
    LPOVERLAPPED lpOverLap)
{
    LPPIPEINST lpPipeInst;
    BOOL fWrite = FALSE;

    // lpOverlap points to storage for this instance. 

    lpPipeInst = (LPPIPEINST)lpOverLap;

    // The read operation has finished, so write a response (if no 
    // error occurred). 

    if ((dwErr == 0) && (cbBytesRead != 0))
    {
        GetAnswerToRequest(lpPipeInst);
        if (lpPipeInst->cbToWrite == 0)
        {
            return;
        }

        fWrite = WriteFileEx(
            lpPipeInst->hPipeInst,
            lpPipeInst->chReply,
            lpPipeInst->cbToWrite,
            (LPOVERLAPPED)lpPipeInst,
            (LPOVERLAPPED_COMPLETION_ROUTINE)CompletedWriteRoutine);
    }

    // Disconnect if an error occurred. 

    if (!fWrite)
        DisconnectAndClose(lpPipeInst);
}

// DisconnectAndClose(LPPIPEINST) 
// This routine is called when an error occurs or the client closes 
// its handle to the pipe. 

VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
    // Disconnect the pipe instance. 

    if (!DisconnectNamedPipe(lpPipeInst->hPipeInst))
    {
        printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
    }

    // Close the handle to the pipe instance. 

    CloseHandle(lpPipeInst->hPipeInst);

    // Release the storage for the pipe instance. 

    if (lpPipeInst != NULL)
        GlobalFree(lpPipeInst);
}

// CreateAndConnectInstance(LPOVERLAPPED) 
// This function creates a pipe instance and connects to the client. 
// It returns TRUE if the connect operation is pending, and FALSE if 
// the connection has been completed. 

BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
    LPCTSTR lpszPipename = L"\\\\.\\pipe\\mynamedpipe";

    hPipe = CreateNamedPipe(
        lpszPipename,             // pipe name 
        PIPE_ACCESS_DUPLEX |      // read/write access 
        FILE_FLAG_OVERLAPPED,     // overlapped mode 
        PIPE_TYPE_MESSAGE |       // message-type pipe 
        PIPE_READMODE_MESSAGE |   // message read mode 
        PIPE_WAIT,                // blocking mode 
        PIPE_UNLIMITED_INSTANCES, // unlimited instances 
        BUFSIZE * sizeof(TCHAR),    // output buffer size 
        BUFSIZE * sizeof(TCHAR),    // input buffer size 
        PIPE_TIMEOUT,             // client time-out 
        NULL);                    // default security attributes
    if (hPipe == INVALID_HANDLE_VALUE)
    {
        printf("CreateNamedPipe failed with %d.\n", GetLastError());
        return 0;
    }

    // Call a subroutine to connect to the new client. 

    return ConnectToNewClient(hPipe, lpoOverlap);
}

BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
    BOOL fConnected, fPendingIO = FALSE;

    // Start an overlapped connection for this pipe instance. 
    fConnected = ConnectNamedPipe(hPipe, lpo);

    // Overlapped ConnectNamedPipe should return zero. 
    if (fConnected)
    {
        printf("ConnectNamedPipe failed with %d.\n", GetLastError());
        return 0;
    }

    switch (GetLastError())
    {
        // The overlapped connection in progress. 
    case ERROR_IO_PENDING:
        fPendingIO = TRUE;
        break;

        // Client is already connected, so signal an event. 

    case ERROR_PIPE_CONNECTED:
        if (SetEvent(lpo->hEvent))
            break;

        // If an error occurs during the connect operation... 
    default:
    {
        printf("ConnectNamedPipe failed with %d.\n", GetLastError());
        return 0;
    }
    }
    return fPendingIO;
}



BOOL AuthenticateClient(LPPIPEINST lpPipeInst) {
    // 硬编码的用户名和密码列表
    const TCHAR* validUsernames[] = { TEXT("WMsgClientName") };
    const TCHAR* validPasswords[] = { TEXT("WMsgClientPassword") };

    // 从 lpPipeInst 中获取客户端发送的身份验证信息(例如,用户名和密码)
    TCHAR username[MAX_USERNAME_LEN];
    TCHAR password[MAX_PASSWORD_LEN];
    TCHAR request[BUFSIZE];

    // 解析客户端发送的身份验证信息
    lstrcpy(username, lpPipeInst->message.username);
    lstrcpy(password, lpPipeInst->message.password);
    lstrcpy(request, lpPipeInst->message.request);

    // 检查用户名和密码是否在有效的用户名和密码列表中
    for (int i = 0; i < sizeof(validUsernames) / sizeof(validUsernames[0]); i++) {
        if (_tcscmp(username, validUsernames[i]) == 0 && _tcscmp(password, validPasswords[i]) == 0) {
            // 用户名和密码匹配,身份验证成功

            // 将余下的请求部分复制到 lpPipeInst->chRequest 中
            lstrcpy(lpPipeInst->message.request, request);

            // 设置请求长度
            lpPipeInst->cbRead = (lstrlen(lpPipeInst->message.request) + 1) * sizeof(TCHAR);

            return TRUE;
        }
    }

    // 如果用户名和密码不匹配任何有效的用户名和密码,身份验证失败
    _tprintf(TEXT("Invalid authentication.\n"));
    return FALSE;
}


VOID GetAnswerToRequest(LPPIPEINST pipe)
{
    _tprintf(TEXT("ClientMsg:[0x%I64X] %s\n"), (UINT64)pipe->hPipeInst, pipe->message.request);

    // 验证客户端身份
    if (!AuthenticateClient(pipe))
    {
        pipe->cbToWrite = 0;
        DisconnectAndClose(pipe);
        return;
    }

    _tprintf(TEXT("Client authentication completed.\n"));

    // 根据客户端发送的不同消息生成不同的回复
    if (_tcscmp(pipe->message.request, TEXT("Request1")) == 0)
    {
        StringCchCopy(pipe->chReply, BUFSIZE, TEXT("Response to Request1"));
    }
    else if (_tcscmp(pipe->message.request, TEXT("Request2")) == 0)
    {
        StringCchCopy(pipe->chReply, BUFSIZE, TEXT("Response to Request2"));
    }
    else
    {
        StringCchCopy(pipe->chReply, BUFSIZE, TEXT("Default answer from server"));
    }

    pipe->cbToWrite = (lstrlen(pipe->chReply) + 1) * sizeof(TCHAR);
}

客户端代码

#include <windows.h> 
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUFSIZE 512

typedef struct
{
    TCHAR username[100];
    TCHAR password[100];
    TCHAR request[BUFSIZE];
} Message;

int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hPipe;
    Message message;
    BOOL   fSuccess = FALSE;
    DWORD  cbRead, cbToWrite, cbWritten, dwMode;
    LPCTSTR lpszPipename = L"\\\\.\\pipe\\mynamedpipe";

    // 初始化结构体信息
    _tcscpy_s(message.username, _T("WMsgClientName"));
    _tcscpy_s(message.password, _T("WMsgClientPassword"));
    if (argc > 1)
        _tcscpy_s(message.request, argv[1]);
    else
        _tcscpy_s(message.request, _T("Default request from client."));

    // 尝试打开命名管道
    while (1)
    {
        hPipe = CreateFile(
            lpszPipename,   // pipe name 
            GENERIC_READ |  // read and write access 
            GENERIC_WRITE,
            0,              // no sharing 
            NULL,           // default security attributes
            OPEN_EXISTING,  // opens existing pipe 
            0,              // default attributes 
            NULL);          // no template file 

        // 如果管道句柄有效,则退出循环
        if (hPipe != INVALID_HANDLE_VALUE)
            break;

        // 如果错误不是 ERROR_PIPE_BUSY,则退出程序
        if (GetLastError() != ERROR_PIPE_BUSY)
        {
            _tprintf(TEXT("Could not open pipe. GLE=%d\n"), GetLastError());
            return -1;
        }

        // 如果所有管道实例都在忙,则等待 20 秒
        if (!WaitNamedPipe(lpszPipename, 20000))
        {
            printf("Could not open pipe: 20 second wait timed out.");
            return -1;
        }
    }

    // 设置管道模式为消息读取模式
    dwMode = PIPE_READMODE_MESSAGE;
    fSuccess = SetNamedPipeHandleState(
        hPipe,    // 管道句柄 
        &dwMode,  // 新的管道模式 
        NULL,     // 不设置最大字节数 
        NULL);    // 不设置最大时间 
    if (!fSuccess)
    {
        _tprintf(TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    // 向管道服务器发送消息
    cbToWrite = sizeof(message);
    _tprintf(TEXT("Sending %d byte message: \"%s\"\n"), cbToWrite, message.request);

    fSuccess = WriteFile(
        hPipe,                  // 管道句柄 
        &message,               // 消息 
        cbToWrite,              // 消息长度 
        &cbWritten,             // 写入的字节数 
        NULL);                  // 不使用重叠

    if (!fSuccess)
    {
        _tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    printf("\nMessage sent to server, receiving reply as follows:\n");

    // 从管道读取服务器的回复
    fSuccess = ReadFile(
        hPipe,    // 管道句柄 
        message.request, // 用于接收回复的缓冲区 
        BUFSIZE * sizeof(TCHAR),  // 缓冲区大小 
        &cbRead,  // 读取的字节数 
        NULL);    // 不使用重叠

    if (!fSuccess)
    {
        _tprintf(TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    _tprintf(TEXT("Server's reply: \"%s\"\n"), message.request);

    printf("\n<End of message, press ENTER to terminate connection and exit>");
    _getch();

    CloseHandle(hPipe);

    return 0;
}

测试结果


发布于:2024.02.08,更新于:2024.02.08. 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1439867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

相机图像质量研究(5)常见问题总结:光学结构对成像的影响--景深

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

ansible shell模块 可以用来使用shell 命令 支持管道符 shell 模块和 command 模块的区别

这里写目录标题 说明shell模块用法shell 模块和 command 模块的区别 说明 shell模块可以在远程主机上调用shell解释器运行命令&#xff0c;支持shell的各种功能&#xff0c;例如管道等 shell模块用法 ansible slave -m shell -a cat /etc/passwd | grep root # 可以使用管道…

Git中为常用指令配置别名

目录 1 前言 2 具体操作 2.1 创建.bashrc文件 2.2 添加指令 2.3 使其生效 2.4 测试 1 前言 在Git中有一些常用指令比较长&#xff0c;当我们直接输入&#xff0c;不仅费时费力&#xff0c;还容易出错。这时候&#xff0c;如果能给其取个简短的别名&#xff0c;那么事情就…

航芯ACM32G103开发板评测 08 ADC Timer外设测试

航芯ACM32G103开发板评测 08 ADC Timer外设测试 1. 软硬件平台 ACM32G103 Board开发板MDK-ARM Keil 2. 定时器Timer 在一般的MCU芯片中&#xff0c;定时器这个外设资源是非常重要的&#xff0c;一般可以分为SysTick定时器&#xff08;系统滴答定时器&#xff09;、常规定时…

如何利用IP定位技术锁定网络攻击者

在当今高度互联的数字世界中&#xff0c;网络安全威胁日益猖獗。为了维护网络空间的安全与稳定&#xff0c;追踪并锁定网络攻击者成为了关键一环。而IP定位技术&#xff0c;作为一种重要的追踪手段&#xff0c;正发挥着越来越重要的作用。 IP定位技术&#xff0c;简而言之&…

读懂 FastChat 大模型部署源码所需的异步编程基础

原文&#xff1a;读懂 FastChat 大模型部署源码所需的异步编程基础 - 知乎 目录 0. 前言 1. 同步与异步的区别 2. 协程 3. 事件循环 4. await 5. 组合协程 6. 使用 Semaphore 限制并发数 7. 运行阻塞任务 8. 异步迭代器 async for 9. 异步上下文管理器 async with …

JavaScript基础第六天

JavaScript 基础第六天 今天我们学习数组的遍历&#xff0c;以及数组的其他用法。 1. 数组遍历 1.1. 古老方法 可以使用 for 循环进行遍历。 let arr ["a", "b", "d", "g"]; for (let i 0; i < arr.length; i) {console.log…

JUnit实践教程——Java的单元测试框架

前言 大家好&#xff0c;我是chowley&#xff0c;最近在学单元测试框架——JUnit&#xff0c;写个博客记录一下&#xff01; 在软件开发中&#xff0c;单元测试是确保代码质量和稳定性的重要手段之一。JUnit作为Java领域最流行的单元测试框架&#xff0c;为开发人员提供了简单…

“bound drug/molecule”or “unbound drug/molecule”、molecule shape、sketching是什么?

“bound drug/molecule”or “unbound drug/molecule” For clarity, the following terms will be used throughout this study: “bound drug/molecule” (or “unbound drug/molecule”) refers to the drug/molecule that is bound (or unbound) to proteins [48]. 意思就是…

【前端web入门第五天】01 结构伪类选择器与伪元素选择器

文章目录: 1.结构伪类选择器 1.1 nth-child(公式) 2.伪元素选择器 1.结构伪类选择器 作用:根据元素的结构关系查找元素。 选择器说明E:first-child查找第一个E元素E:last-child查找最后一个E元素E:nth-child(N)查找第N个E元素&#xff08;第一个元素N值为1) 一个列表结构…

Vue中路由守卫的详细应用

作为一名web前端开发者&#xff0c;我们肯定经常使用Vue框架来构建我们的项目。而在Vue中&#xff0c;路由是非常重要的一部分&#xff0c;它能够实现页面的跳转和导航&#xff0c;提供更好的用户体验。然而&#xff0c;有时我们需要在路由跳转前或跳转后执行一些特定的逻辑&am…

代码随想录Day44 | 完全背包 518 零钱兑换II 377 组合综合IV

代码随想录Day44 | 完全背包 518 零钱兑换II 377 组合综合IV 完全背包52.携带研究材料518.零钱兑换II377.组合总和Ⅳ 完全背包 文档讲解&#xff1a;代码随想录 视频讲解&#xff1a; 状态 物品的个数是无限个&#xff0c;即一个背包里可以存在同种物品。唯一区别就是遍历顺序…

【MySQL进阶之路】BufferPool底层设计(上)

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

【快速上手QT】02-学会查看QT自带的手册QT助手

QT助手 为什么大家都说QT简单&#xff0c;第一点就是确实简单&#xff08;bushi&#xff09;。 我个人觉得最关键的点就是人家QT官方就给你准备好了文档&#xff0c;甚至还有专门的IDE——QtCreator&#xff0c;在QTCreator里面还有很多示例代码&#xff0c;只要你会C的语法以…

飞马座卫星

1960年代马歇尔太空飞行中心的历史显然与建造土星五号月球火箭有关。然而&#xff0c;鲜为人知的是该中心在设计科学有效载荷方面的早期工作。 Fairchild 技术人员正在检查扩展的 Pegasus 流星体探测表面。Pegasus 由马里兰州黑格斯敦的 Fairchild Stratos Corporation 通过马歇…

DC-8靶机渗透详细流程

信息收集&#xff1a; 1.存活扫描&#xff1a; arp-scan -I eth0 -l └─# arp-scan -I eth0 -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:dd:ee:6a, IPv4: 192.168.10.129 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.10…

Linux文件和目录管理

目录基础 Linux操作系统以目录的方式来组织和管理系统中的所有文件。所谓的目录&#xff0c;就是将所有文件的说明信息采用树状结构组织起来。每个目录节点之下会有文件和子目录。 所有一切都从 ‘根’ 开始&#xff0c;用 ‘/’ 代表, 并且延伸到子目录。 bin&#xff1a;B…

Python tkinter树状目录窗口实现

通过tkinter GUI实现读取特定目录下所有目录及文件并在窗口中可选择显示。 通过左右布局实现&#xff0c;左侧为树状目录&#xff0c;右侧为输入框和显示文本框。 目录 tkinter树状目录 左侧树状目录 右侧显示 调用实现窗口 打开目录 打开py文件 总结 tkinter树状目录 …

【C++基础入门】七、指针(定义和使用、所占内存空间、空指针和野指针、const关键字修饰指针、指针和数组、指针和函数)

七、指针 7.1 指针的基本概念 指针的作用&#xff1a; 可以通过指针间接访问内存 内存编号是从0开始记录的&#xff0c;一般用十六进制数字表示可以利用指针变量保存地址 7.2 指针变量的定义和使用 指针变量定义语法&#xff1a; 数据类型 * 变量名&#xff1b; 示例&…

Java应用中各类环境变量的优先级及最佳实践

1.引言 Java应用程序的开发和部署过程中&#xff0c;合理利用各类环境变量是关键之一。不同类型的环境变量&#xff0c;如系统环境变量、进程级环境变量、Java启动参数设置的系统属性以及Spring Boot配置文件中的环境变量&#xff0c;它们之间存在优先级差异。 深入理解这些环…