命名管道是一种进程间通信(RPC)的方式,类似于socket,命名管道的一端为server,另一端为client,client与server之间支持单向或双向通信。与socket相比,命名管道更适合本地进程间的通信,使用更方便和高效。
与socket相同,命名管道在通信之前,客户端和服务器端必须建立连接。好比两个人约会,需要事先商量好一个时间在某一个地点(如:某某咖啡厅)见面,见了面后就可以愉快的畅谈了。
命名管道的名字类似于这个约好的咖啡厅,通过这个名字,客户端与服务端建立连接。
谁是客户端谁是服务端,建立连接的过程是怎样的? 还是以约会为例,两个人约好了时间在某个咖啡厅见面,肯定有一个人先到有一个人后到,如果先到的人到了咖啡厅看对方没来,认为对方爽约,就立即离开了,则这样的约会一定会失败。所以为了能成功约会,先到的人到了咖啡厅应该等待一段时间,半个小时,1个小时,1天,甚至可以等上10年(忠诚的八公)。这样一般会等到另一个人到来,约会成功,下面就可以愉快的交谈了。先来的人就是服务端,他需要的特殊操作是等待,后来的人是客户端,他来到咖啡厅后,要找到等待他的人,客户端的特殊操作就是连接。
我封装一个类CPipe,利用Windows提供的API实现了Pipe连接和通信的功能。
CreatePipe是服务端调用,创建命名管道,为防止重复创建,需要先测试该命名管道是否已经创建(可以使用WaitNamedPipe测试)
BOOL CPipe::CreatePipe(const char* pipename)
{
if(m_hPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hPipe);
m_hPipe = INVALID_HANDLE_VALUE;
}
//创建命名管道
m_hPipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX |
(OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0), PIPE_TYPE_BYTE |
PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0,
60000, NULL);
if(m_hPipe == INVALID_HANDLE_VALUE)
{
WT_Error("CPipe::CreatePipe: lasterr=%d\n", GetLastError());
return FALSE;
}
CreateThread(0, 0, PipeServerListenProc, this, 0, 0);
WaitForSingleObject(m_hEvent, 2000); //wait child thread running
return TRUE;
}
客户端使用OpenPipe连接命名管道:
BOOL CPipe::OpenPipe(const char* pipename)
{
if(m_hPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hPipe);
m_hPipe = INVALID_HANDLE_VALUE;
}
m_hPipe = ::CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0, NULL);
if(m_hPipe == INVALID_HANDLE_VALUE)
{
WT_Error("CPipe::CreatePipe: lasterr=%d\n", GetLastError());
return FALSE;
}
m_bConnected = TRUE;
CreateThread(0, 0, PipeClientListenProc, this, 0, 0);
WaitForSingleObject(m_hEvent, 2000); //wait child thread running
return TRUE;
}
如果不想区分服务端和客户端,即:不管是客户端还是服务端用同一个函数完成管道的连接,可使用BindPipe:
BOOL CPipe::BindPipe(DWORD dwPortId)
{
char pipename[256];
sprintf(pipename, PIPENAME, dwPortId);
WT_Trace("BindPipe: name=%s\n", pipename);
if (!WaitNamedPipe(pipename, NMPWAIT_USE_DEFAULT_WAIT))
{
//若命名管道不存在则创建(将成为服务端)
return CreatePipe(dwPortId);
}
else
{
//若命名管道存在则连接(将成为客户端)
return OpenPipe(dwPortId);
}
}