基于visual c++之windows核心编程代码分析(17)通过pipe进程间通信

本文深入探讨了Windows系统下的命名管道通信机制,包括其工作原理、客户端和服务端的代码实现细节,以及如何通过命名管道实现进程间的数据交换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://tech.ddvip.com/2012-12/1355150688186856.html

管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。

   管道分为匿名管道和命名管道。

   匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。

   命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。

管道客户端代码实现如下

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/* 头文件 */
#include <windows.h>     
#include <stdio.h>    
#include <conio.h>    
#include <tchar.h>    
/* 常量 */
#define BUFSIZE 512    
/* ************************************   
* int main(VOID)    
* 功能    pipe 通信服务端主函数   
**************************************/
int main(int argc, TCHAR *argv[])     
{     
    HANDLE hPipe;     
    LPTSTR lpvMessage=TEXT("Default message from client");     
    TCHAR chBuf[BUFSIZE];     
    BOOL fSuccess;     
    DWORD cbRead, cbWritten, dwMode;     
    LPTSTR lpszPipename = TEXT("\.pipesamplenamedpipe");     
          
    if( argc > 1 )  // 如果输入了参数,则使用输入的参数    
        lpvMessage = argv[1];    
    while(1)     
    {     
        // 打开一个命名pipe    
        hPipe = CreateFile(     
            lpszPipename,  // pipe 名     
            GENERIC_READ |   GENERIC_WRITE,    //  可读可写    
            0,             // 不共享    
            NULL,          // 默认安全属性    
            OPEN_EXISTING, // 已经存在(由服务端创建)    
            0,             // 默认属性    
            NULL);        
        if(hPipe != INVALID_HANDLE_VALUE)     
            break;     
          
        // 如果不是 ERROR_PIPE_BUSY 错误,直接退出      
        if(GetLastError() != ERROR_PIPE_BUSY)     
        {    
            printf("Could not open pipe");     
            return0;    
        }    
          
        // 如果所有pipe实例都处于繁忙状态,等待2秒。    
        if(!WaitNamedPipe(lpszPipename, 2000))     
        {     
            printf("Could not open pipe");     
            return0;    
        }     
    }     
          
    // pipe已经连接,设置为消息读状态     
    dwMode = PIPE_READMODE_MESSAGE;     
    fSuccess = SetNamedPipeHandleState(     
        hPipe,   // 句柄    
        &dwMode, // 新状态    
        NULL,    // 不设置最大缓存    
        NULL);   // 不设置最长时间    
    if(!fSuccess)     
    {    
        printf("SetNamedPipeHandleState failed");     
        return0;    
    }    
          
    // 写入pipe    
    fSuccess = WriteFile(     
        hPipe,                 // 句柄    
        lpvMessage,            // 写入的内容    
        (lstrlen(lpvMessage)+1)*sizeof(TCHAR),// 写入内容的长度    
        &cbWritten,            // 实际写的内容    
        NULL);                 // 非 overlapped     
    if(!fSuccess)     
    {    
        printf("WriteFile failed");     
        return0;    
    }    
          
    do 
    {     
        // 读回复     
        fSuccess = ReadFile(     
            hPipe,   // 句柄    
            chBuf,   // 读取内容的缓存    
            BUFSIZE*sizeof(TCHAR), // 缓存大小    
            &cbRead, // 实际读的字节    
            NULL);   // 非 overlapped     
          
        if(! fSuccess && GetLastError() != ERROR_MORE_DATA)     
            break;//失败,退出    
          
        _tprintf( TEXT("%sn"), chBuf );// 打印读的结果    
    }while (!fSuccess); //  ERROR_MORE_DATA 或者成功则循环    
          
    getch();//任意键退出    
    // 关闭句柄    
    CloseHandle(hPipe);      
    return0;     
}

管道服务端代码实现如下,

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/* 头文件 */
#include <windows.h>     
#include <stdio.h>    
#include <tchar.h>    
/* 常量 */
#define PIPE_TIMEOUT 5000    
#define BUFSIZE 4096    
/* 结构定义 */
typedef struct 
{     
    OVERLAPPED oOverlap;     
    HANDLE hPipeInst;     
    TCHAR chRequest[BUFSIZE];     
    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 main(VOID)    
* 功能    pipe 通信服务端主函数   
**************************************/
int main(VOID)     
{     
    HANDLE hConnectEvent;     
    OVERLAPPED oConnect;     
    LPPIPEINST lpPipeInst;     
    DWORD dwWait, cbRet;     
    BOOL fSuccess, fPendingIO;     
          
    // 用于连接操作的事件对象     
    hConnectEvent = CreateEvent(     
        NULL,   // 默认属性    
        TRUE,   // 手工reset    
        TRUE,   // 初始状态 signaled     
        NULL);  // 未命名    
          
    if(hConnectEvent == NULL)     
    {    
        printf("CreateEvent failed with %d.n", GetLastError());     
        return0;    
    }    
    // OVERLAPPED 事件    
    oConnect.hEvent = hConnectEvent;     
          
    // 创建连接实例,等待连接    
    fPendingIO = CreateAndConnectInstance(&oConnect);     
          
    while(1)     
    {    
        // 等待客户端连接或读写操作完成     
        dwWait = WaitForSingleObjectEx(     
            hConnectEvent, // 等待的事件     
            INFINITE,      // 无限等待    
            TRUE);           
          
        switch(dwWait)     
        {     
        case0:         
            // pending     
            if(fPendingIO)     
            {     
                // 获取 Overlapped I/O 的结果    
                fSuccess = GetOverlappedResult(     
                    hPipe,    // pipe 句柄     
                    &oConnect,// OVERLAPPED 结构     
                    &cbRet,   // 已经传送的数据量    
                    FALSE);   // 不等待    
                if(!fSuccess)     
                {    
                    printf("ConnectNamedPipe (%d)n", GetLastError());     
                    return0;    
                }    
            }     
          
            // 分配内存     
            lpPipeInst = (LPPIPEINST) HeapAlloc(GetProcessHeap(),0,sizeof(PIPEINST));     
            if(lpPipeInst == NULL)     
            {    
                printf("GlobalAlloc failed (%d)n", GetLastError());     
                return0;    
            }    
            lpPipeInst->hPipeInst = hPipe;     
          
            // 读和写,注意CompletedWriteRoutine和CompletedReadRoutine的相互调用    
            lpPipeInst->cbToWrite = 0;     
            CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst);     
          
            // 再创建一个连接实例,以响应下一个客户端的连接    
            fPendingIO = CreateAndConnectInstance(     
                &oConnect);     
            break;     
          
            // 读写完成     
        caseWAIT_IO_COMPLETION:     
            break;     
          
        default:     
            {    
                printf("WaitForSingleObjectEx (%d)n", GetLastError());     
                return0;    
            }    
        }     
    }     
    return0;     
}     
          
/* ************************************   
* CompletedWriteRoutine    
*   写入pipe操作的完成函数   
*   接口参见FileIOCompletionRoutine回调函数定义   
*   
*   当写操作完成时被调用,开始读另外一个客户端的请求   
**************************************/
VOID WINAPI CompletedWriteRoutine(    
                                  DWORD dwErr,     
                                  DWORD cbWritten,     
                                  LPOVERLAPPED lpOverLap)     
{     
    LPPIPEINST lpPipeInst;     
    BOOL fRead = FALSE;     
    // 保存overlap实例    
    lpPipeInst = (LPPIPEINST) lpOverLap;     
          
// 如果没有错误    
    if((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))     
    {           
        fRead = ReadFileEx(     
        lpPipeInst->hPipeInst,     
        lpPipeInst->chRequest,     
        BUFSIZE*sizeof(TCHAR),     
        (LPOVERLAPPED) lpPipeInst,     
        // 写读操作完成后,调用CompletedReadRoutine    
        (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);     
    }       
    if(! fRead)     
        // 出错,断开连接    
        DisconnectAndClose(lpPipeInst);     
}     
          
/* ************************************   
* CompletedReadRoutine    
*   读取pipe操作的完成函数   
*   接口参见FileIOCompletionRoutine回调函数定义   
*   
*   当读操作完成时被调用,写入回复   
**************************************/
VOID WINAPI CompletedReadRoutine(    
                                 DWORD dwErr,     
                                 DWORD cbBytesRead,     
                                 LPOVERLAPPED lpOverLap)     
{     
    LPPIPEINST lpPipeInst;     
    BOOL fWrite = FALSE;     
          
    // 保存overlap实例    
    lpPipeInst = (LPPIPEINST) lpOverLap;     
          
    // 如果没有错误    
    if((dwErr == 0) && (cbBytesRead != 0))     
    {     
        // 根据客户端的请求,生成回复    
        GetAnswerToRequest(lpPipeInst);     
        // 将回复写入到pipe    
        fWrite = WriteFileEx(     
            lpPipeInst->hPipeInst,     
            lpPipeInst->chReply,//将响应写入pipe    
            lpPipeInst->cbToWrite,     
            (LPOVERLAPPED) lpPipeInst,     
            // 写入完成后,调用CompletedWriteRoutine    
            (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);     
    }     
          
    if(! fWrite)     
        // 出错,断开连接    
        DisconnectAndClose(lpPipeInst);     
}     
          
/* ************************************   
* VOID DisconnectAndClose(LPPIPEINST lpPipeInst)    
* 功能    断开一个连接的实例   
* 参数    lpPipeInst,断开并关闭的实例句柄   
**************************************/
VOID DisconnectAndClose(LPPIPEINST lpPipeInst)     
{     
    // 关闭连接实例    
    if(! DisconnectNamedPipe(lpPipeInst->hPipeInst) )     
    {    
        printf("DisconnectNamedPipe failed with %d.n", GetLastError());    
    }    
    // 关闭 pipe 实例的句柄     
    CloseHandle(lpPipeInst->hPipeInst);     
    // 释放    
    if(lpPipeInst != NULL)     
        HeapFree(GetProcessHeap(),0, lpPipeInst);     
}     
          
/* ************************************   
* BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)   
* 功能    建立连接实例   
* 参数    lpoOverlap,用于overlapped IO的结构   
* 返回值   是否成功   
**************************************/
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)     
{     
    LPTSTR lpszPipename = TEXT("\.pipesamplenamedpipe");     
    // 创建named pipe      
    hPipe = CreateNamedPipe(     
        lpszPipename,            // pipe 名     
        PIPE_ACCESS_DUPLEX |     // 可读可写    
        FILE_FLAG_OVERLAPPED,    // overlapped 模式     
        // pipe模式    
        PIPE_TYPE_MESSAGE |      // 消息类型 pipe     
        PIPE_READMODE_MESSAGE |  // 消息读模式     
        PIPE_WAIT,               // 阻塞模式    
        PIPE_UNLIMITED_INSTANCES,// 无限制实例    
        BUFSIZE*sizeof(TCHAR),   // 输出缓存大小    
        BUFSIZE*sizeof(TCHAR),   // 输入缓存大小    
        PIPE_TIMEOUT,            // 客户端超时    
        NULL);                   // 默认安全属性    
    if(hPipe == INVALID_HANDLE_VALUE)     
    {    
        printf("CreateNamedPipe failed with %d.n", GetLastError());     
        return0;    
    }    
          
    // 连接到新的客户端    
    returnConnectToNewClient(hPipe, lpoOverlap);     
}    
          
/* ************************************   
* BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)   
* 功能    建立连接实例   
* 参数    lpoOverlap,用于overlapped IO的结构   
* 返回值   是否成功   
**************************************/
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)     
{     
    BOOL fConnected, fPendingIO = FALSE;     
          
    // 开始一个 overlapped 连接     
    fConnected = ConnectNamedPipe(hPipe, lpo);     
          
    if(fConnected)     
    {    
        printf("ConnectNamedPipe failed with %d.n", GetLastError());     
        return0;    
    }    
    switch(GetLastError())     
    {     
        // overlapped连接进行中.     
    caseERROR_IO_PENDING:     
        fPendingIO = TRUE;     
        break;     
        // 已经连接,因此Event未置位     
    caseERROR_PIPE_CONNECTED:     
        if(SetEvent(lpo->hEvent))     
            break;     
        // error    
    default:     
        {    
            printf("ConnectNamedPipe failed with %d.n", GetLastError());    
            return0;    
        }    
    }     
    returnfPendingIO;     
}    
          
// TODO根据客户端的请求,给出响应    
VOID GetAnswerToRequest(LPPIPEINST pipe)    
{    
    _tprintf( TEXT("[%d] %sn"), pipe->hPipeInst, pipe->chRequest);    
    lstrcpyn( pipe->chReply,  TEXT("Default answer from server") ,BUFSIZE);    
    pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值