- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
总结
对于命名管道来说的话,简单理解的话,其实是可以将其看做是一种 Socket 的,
而对于命名管道也就是那几个 API 在使用,对于一些不常用的 API ,
感兴趣的也可以从 MSDN 中获取到这部分信息。
对于进程间的通信的话,其实也就可以利用介绍的这四种方式来实现了,
第一种是利用剪贴板实现本机进程间的通信。
第二种是利用邮槽实现本机或跨网络进程间的通信。
第三种是利用匿名管道实现本机父子进程之间的通信。
第四种是利用命名管道实现本机或跨网络进程间的通信。
然后的话,我还打算介绍一种比较偏门的实现进程间通信的手段,
当然,这要到下一篇博文中才会作出介绍。
最后的话,就是在前面的一篇博文中有一位朋友说可以利用 WCF 来实现进程之间的通信,
这个呢理论上是可以实现的,但是本人也没有做过这方面的 Demo ,
所以估计得看以后有时间的话,也可以拿过来写写文章的。
命名管道概述
命名管道是通过网络来完成进程之间的通信的,命名管道依赖于底层网络接口,
其中包括有 DNS 服务,TCP/IP 协议等等机制,但是其屏蔽了底层的网络协议细节,
对于匿名管道而言,其只能实现在父进程和子进程之间进行通信,而对于命名管道而言,
其不仅可以在本地机器上实现两个进程之间的通信,还可以跨越网络实现两个进程之间的通信。
命名管道使用了 Windows 安全机制,因而命名管道的服务端可以控制哪些客户有权与其建立连接,
而哪些客户端是不能够与这个命名管道建立连接的。
利用命名管道机制实现不同机器上的进程之间相互进行通信时,
可以将命名管道作为一种网络编程方案时,也就是看做是 Socket 就可以了,
它实际上是建立了一个客户机/服务器通信体系,并在其中可靠的传输数据。
命名管道的通信是以连接的方式来进行的,
服务器创建一个命名管道对象,然后在此对象上等待连接请求,
一旦客户连接过来,则两者都可以通过命名管道读或者写数据。
命名管道提供了两种通信模式:字节模式和消息模式。
在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动,
而在消息模式下,客户机和服务器则通过一系列的不连续的数据单位,进行数据的收发,
每次在管道上发出一个消息后,它必须作为一个完整的消息读入。
命名管道使用流程
服务端:
服务端进程调用 CreateNamedPipe 函数来创建一个有名称的命名管道,
在创建命名管道的时候必须指定一个本地的命名管道名称(不然就不叫命名管道了),
Windows 允许同一个本地的命名管道名称有多个命名管道实例,
所以,服务器进程在调用 CreateNamedPipe 函数时必须指定最大允许的实例数(0 -255),
如果 CreateNamedPipe 函数成功返回后,服务器进程得到一个指向一个命名管道实例的句柄,
然后,服务器进程就可以调用 ConnectNamedPipe 来等待客户的连接请求,
这个 ConnectNamedPipe 既支持同步形式,又支持异步形式,
若服务器进程以同步形式调用 ConnectNamedPipe 函数,
(同步方式也就是如果没有得到客户端的连接请求,则会一直等到)
那么,当该函数返回时,客户端与服务器之间的命名管道连接也就已经建立起来了。
在已经建立了连接的命名管道实例中,
服务端进程就会得到一个指向该管道实例的句柄,这个句柄称之为服务端句柄。
同时,服务端进程可以调用 DisconnectNamedPipe 函数,
将一个管道实例与当前建立连接的客户端进程断开,从而可以重新连接到新的客户进程。
当然在服务端也是可以调用 CloseHandle 来关闭一个已经建立连接的命名管道实例。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 512
DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);
int _tmain(VOID)
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
for (;;)
{
_tprintf( TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
printf("Client connected, creating a processing thread.\n");
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
_tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
return -1;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return 0;
}
DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
HANDLE hHeap = GetProcessHeap();
TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// Do some extra error checking since the app will keep running even if this
// thread fails.
if (lpvParam == NULL)
{
printf( "\nERROR - Pipe Server Failure:\n");
printf( " InstanceThread got an unexpected NULL value in lpvParam.\n");
printf( " InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
if (pchRequest == NULL)
{
printf( "\nERROR - Pipe Server Failure:\n");
printf( " InstanceThread got an unexpected NULL heap allocation.\n");
printf( " InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
return (DWORD)-1;
}
if (pchReply == NULL)
{
printf( "\nERROR - Pipe Server Failure:\n");
printf( " InstanceThread got an unexpected NULL heap allocation.\n");
printf( " InstanceThread exitting.\n");
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
// Print verbose messages. In production code, this should be for debugging only.
printf("InstanceThread created, receiving and processing messages.\n");
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
_tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
}
else
{
_tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
}
break;
}
// Process the incoming message.
GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || cbReplyBytes != cbWritten)
{
_tprintf(TEXT("InstanceThread WriteFile failed, GLE=%d.\n"), GetLastError());
break;
}
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
printf("InstanceThread exitting.\n");
return 1;
}
VOID GetAnswerToRequest( LPTSTR pchRequest,
LPTSTR pchReply,
LPDWORD pchBytes )
// This routine is a simple function to print the client request to the console
// and populate the reply buffer with a default data string. This is where you
// would put the actual client request processing code that runs in the context
// of an instance thread. Keep in mind the main thread will continue to wait for
// and receive other client connections while the instance thread is working.
{
_tprintf( TEXT("Client Request String:\"%s\"\n"), pchRequest );
// Check the outgoing message to make sure it's not too long for the buffer.
if (FAILED(StringCchCopy( pchReply, BUFSIZE, TEXT("default answer from server") )))
{
*pchBytes = 0;
pchReply[0] = 0;
printf("StringCchCopy failed, no outgoing message.\n");
return;
}
*pchBytes = (lstrlen(pchReply)+1)*sizeof(TCHAR);
}
客户端
客户端进程调用 CreateFile 函数连接到一个正在等待连接的命名管道上,
在这里客户端需要指定将要连接的命名管道的名称,
当 CreateFile 成功返回后,客户进程就得到了一个指向已经建立连接的命名管道实例的句柄,
到这里,服务器进程的 ConnectNamedPipe 也就完成了其建立连接的任务。
客户端进程除了调用 CreateFile 函数来建立管道连接以外,
还可以调用 WaitNamedPipe 函数来测试指定名称的管道实例是否可用。
在已经建立了连接的命名管道实例中,客户端进程就会得到一个指向该管道实例的句柄,
这个句柄称之为客户端句柄。
在客户端可以调用 CloseHandle 来关闭一个已经建立连接的命名管道实例。
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
LPTSTR lpvMessage=TEXT("Default message from client.");
TCHAR chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if( argc > 1 )
lpvMessage = argv[1];
// Try to open a named pipe; wait for it, if necessary.
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
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
return -1;
}
// All pipe instances are busy, so wait for 20 seconds.
if ( ! WaitNamedPipe(lpszPipename, 20000))
{
printf("Could not open pipe: 20 second wait timed out.");
return -1;
}
}
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if ( ! fSuccess)
{
_tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
return -1;
}
// Send a message to the pipe server.
cbToWrite = (lstrlen(lpvMessage)+1)*sizeof(TCHAR);
_tprintf( TEXT("Sending %d byte message: \"%s\"\n"), cbToWrite, lpvMessage);
fSuccess = WriteFile(
hPipe, // pipe handle
lpvMessage, // message
cbToWrite, // message length
&cbWritten, // bytes written
NULL); // not overlapped
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
printf("\nMessage sent to server, receiving reply as follows:\n");
do
{
// Read from the pipe.
fSuccess = ReadFile(
hPipe, // pipe handle
chBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbRead, // number of bytes read
NULL); // not overlapped
if ( ! fSuccess && GetLastError() != ERROR_MORE_DATA )
break;
_tprintf( TEXT("\"%s\"\n"), chBuf );
} while ( ! fSuccess); // repeat loop if ERROR_MORE_DATA
if ( ! fSuccess)
{
_tprintf( TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();
CloseHandle(hPipe);
return 0;
} |
|