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" ); return 0;
} // 如果所有pipe实例都处于繁忙状态,等待2秒。 if (!WaitNamedPipe(lpszPipename, 2000))
{ printf( "Could not open pipe" ); return 0;
} } // pipe已经连接,设置为消息读状态 dwMode = PIPE_READMODE_MESSAGE; fSuccess = SetNamedPipeHandleState( hPipe, // 句柄
&dwMode, // 新状态
NULL, // 不设置最大缓存
NULL); // 不设置最长时间
if (!fSuccess)
{ printf( "SetNamedPipeHandleState failed" ); return 0;
} // 写入pipe fSuccess = WriteFile( hPipe, // 句柄
lpvMessage, // 写入的内容
(lstrlen(lpvMessage)+1)* sizeof (TCHAR), // 写入内容的长度
&cbWritten, // 实际写的内容
NULL); // 非 overlapped
if (!fSuccess)
{ printf( "WriteFile failed" ); return 0;
} 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); return 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
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()); return 0;
} // OVERLAPPED 事件 oConnect.hEvent = hConnectEvent; // 创建连接实例,等待连接 fPendingIO = CreateAndConnectInstance(&oConnect); while (1)
{ // 等待客户端连接或读写操作完成 dwWait = WaitForSingleObjectEx( hConnectEvent, // 等待的事件
INFINITE, // 无限等待
TRUE); switch (dwWait)
{ case 0:
// pending if (fPendingIO)
{ // 获取 Overlapped I/O 的结果 fSuccess = GetOverlappedResult( hPipe, // pipe 句柄
&oConnect, // OVERLAPPED 结构
&cbRet, // 已经传送的数据量
FALSE); // 不等待
if (!fSuccess)
{ printf( "ConnectNamedPipe (%d)n" , GetLastError()); return 0;
} } // 分配内存 lpPipeInst = (LPPIPEINST) HeapAlloc(GetProcessHeap(),0, sizeof (PIPEINST)); if (lpPipeInst == NULL)
{ printf( "GlobalAlloc failed (%d)n" , GetLastError()); return 0;
} lpPipeInst->hPipeInst = hPipe; // 读和写,注意CompletedWriteRoutine和CompletedReadRoutine的相互调用 lpPipeInst->cbToWrite = 0; CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst); // 再创建一个连接实例,以响应下一个客户端的连接 fPendingIO = CreateAndConnectInstance( &oConnect); break ; // 读写完成 case WAIT_IO_COMPLETION:
break ; default : { printf( "WaitForSingleObjectEx (%d)n" , GetLastError()); return 0;
} } } return 0;
} /* ************************************ * 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()); return 0;
} // 连接到新的客户端 return ConnectToNewClient(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()); return 0;
} switch (GetLastError())
{ // overlapped连接进行中. case ERROR_IO_PENDING:
fPendingIO = TRUE; break ; // 已经连接,因此Event未置位 case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break ; // error default : { printf( "ConnectNamedPipe failed with %d.n" , GetLastError()); return 0;
} } return fPendingIO;
} // 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); } |