简介
有时候我们需要一个程序与另一个程序之间进行通信。你可能在多台机器上有多个服务器运行,而在一个中央位置对一个或多个服务器进行远程监控。Windows 平台为我们提供了一系列通信方式,从Socket 到命名管道,DDE,DCOM ,邮槽等。本文中作者将探讨邮槽这一通信机制,假定读者熟悉CreateFile(),ReadFile(),WriteFile() 等API 函数,此外还假定读者熟悉重叠I/O 的基础知识。
邮槽
实现一个“ 多写者/ 单读者” 协议。一个进程通过指定一个名称创建一个邮槽,然后等待有消息写入到邮槽中。若其他进程知道邮槽的名称,则可以打开邮槽,往邮槽中写入消息。只能有一个邮槽读者,但可以有多个写者。微软使用服务器/ 客户来描述。服务器创建邮槽并从中读取消息。客户连接到一个已经存在的邮槽,并往里面写入消息。
邮槽有一个有趣而有用的属性。一个进程往邮槽中写入一个消息,读者就可以接收到消息。消息是一整块任意长度的数据,若写者写入60 字节,读者读到60 字节,不多不少。若写者写327 字节,读者读到327 字节。这是一个面向消息的协议,而不是面向字节的协议。这类似于命名管道上的消息模式。这并不是说你不能只读取消息的一部分,只不过使用邮槽的“ 自然” 方式是面向消息的,这在对读者可用的API 里反映出来。
邮槽可以跨网络使用。若在同一台机器上,则你可以给邮槽命名为""."mailslot"slotname 。若跨网络使用,则你可以将”.” 替换为创建邮槽的机器的名称。
创建邮槽
这通过CreateMailslot() 函数完成,第一个参数指明了邮槽名称。其他参数依次是能写入邮槽中的消息的最大大小,邮槽读者等待消息的时间,一个指明句柄是否被子进程继承的安全描述符。
连接邮槽
你可以使用CreateFile() 函数,在其中指定邮槽名称来实现。若你希望实现多写者/ 单读者模式,你必须在打开邮槽时小心共享模式。若邮槽写者打开邮槽时没有指定FILE_SHARE_WRITE 为共享模式,那么它将阻止其他任何写者往邮槽里写入消息。
邮槽的句柄,何以处之?
若你通过CreateMailslot() 函数创建邮槽,你可以使用ReadFile() 从中读取消息。邮槽句柄在重叠I/O 模式中创建,因此你可以在它上面使用重叠I/O ,当然若合适的话,你也可以使用非重叠I/O 模式。你可以调用GetMailslotInfo() 函数来查询有等待被读取的消息的个数,下一个消息的长度,读取消息的超时时限。你可以调用SetMailslotInfo() 函数来改变超时时限。注意你传递给这两个函数的句柄必须是通过CreateMailslot() 创建的。
若你没有创建邮槽,然后你使用CreateFile() 函数连接到邮槽上。这种情况下你可以使用WriteFile() 函数往邮槽里写入消息。你能否使用重叠I/O 模式取决于你如何调用CreateFile() 函数的方式。它可以是同步的,也可以是异步的。你无法使用CreateFile() 函数连接到一个邮槽上并且从邮槽上读取消息。
MSDN 关于的邮槽的文档说只要邮槽上有任何一个打开句柄,邮槽就会存在。但作者说这不一定正确(在win xp sp2 上)。你可以有任何数量的打开的邮槽写句柄,但只要读者句柄一关闭,邮槽就消失了(一旦读句柄关闭,往邮槽中写入消息就会失败)。这是有意义的。因为你只有一个读者,它一旦走了,那么任何写入的消息都只会被系统无意义地缓存。若没有读者了,那么缓存的消息就会永远悬停了(记住你无法使用CreateFile() 来打开邮槽的读句柄)。
可以连接到特定域的特定名字的所有邮槽。这通过指定邮槽名称为“""domainname"mailslot"name“ 。也可以使用”*” 作为首要域。这看起来不错,你可以在一个域内运行的多台机器上的任何数量的读者,并且指定域名同时往邮槽中写入消息。但有个问题,若你使用域名作为邮槽的写者,你没法写入大于424 字节的消息。
BOOLMakeslot()
{ // 创建邮槽 CStringlpszSlotName = _T( " .//mailslot//sample_mailslot " ); // Themailslothandle"hSlot1"isdeclaredglobally. hSlot1 = CreateMailslot(lpszSlotName.GetBuffer( 10 ), 0 , // nomaximummessagesize MAILSLOT_WAIT_FOREVER, // notime-outforoperations (LPSECURITY_ATTRIBUTES)NULL); // nosecurityattributes if (hSlot1 == INVALID_HANDLE_VALUE) { return FALSE; } return TRUE; }
BOOLReadslot()
{ // 读邮槽 DWORDcbMessage,cMessage,cbRead; BOOLfResult; LPWSTRlpszBuffer; TCHARachID[ 80 ]; DWORDcAllMessages; HANDLEhEvent; OVERLAPPEDov; cbMessage = cMessage = cbRead = 0 ; hEvent = CreateEvent(NULL,FALSE,FALSE,_T( " ExampleSlot " )); ov.Offset = 0 ; ov.OffsetHigh = 0 ; ov.hEvent = hEvent; // Mailslothandle"hSlot1"isdeclaredglobally. fResult = GetMailslotInfo(hSlot1, // mailslothandle (LPDWORD)NULL, // nomaximummessagesize & cbMessage, // sizeofnextmessage & cMessage, // numberofmessages (LPDWORD)NULL); // noreadtime-out if ( ! fResult) { // ErrorHandler(hwnd,"GetMailslotInfo"); return FALSE; } if (cbMessage == MAILSLOT_NO_MESSAGE) { // TextOut(hdc,10,10,"Nowaitingmessages.",20); return TRUE; } cAllMessages= cMessage; while (cMessage != 0 ) // retrieveallmessages { // Createamessage-numberstring. wsprintf((LPWSTR)achID, _T( " /nMessage#%dof%d/n " ),cAllMessages - cMessage + 1 , cAllMessages); // Allocatememoryforthemessage. lpszBuffer = (LPWSTR)GlobalAlloc(GPTR, lstrlen((LPWSTR)achID) + cbMessage); lpszBuffer[ 0 ] = ' /0 ' ; fResult = ReadFile(hSlot1, lpszBuffer, cbMessage, & cbRead, & ov); if ( ! fResult) { // ErrorHandler(hwnd,"ReadFile"); GlobalFree((HGLOBAL)lpszBuffer); return FALSE; } // Concatenatethemessageandthemessage-numberstring. lstrcat(lpszBuffer,(LPWSTR)achID); GlobalFree((HGLOBAL)lpszBuffer); fResult = GetMailslotInfo(hSlot1, // mailslothandle (LPDWORD)NULL, // nomaximummessagesize & cbMessage, // sizeofnextmessage & cMessage, // numberofmessages (LPDWORD)NULL); // noreadtime-out if ( ! fResult) { return FALSE; } }return TRUE; }
BOOLWriteslot()
{ // 写邮槽 CStringlpszMessage = _T( " Messageforsample_mailslotinprimarydomain. " ); BOOLfResult; HANDLEhFile; DWORDcbWritten; hFile = CreateFile(_T( " .//mailslot//sample_mailslot " ), GENERIC_WRITE, FILE_SHARE_READ, // requiredtowritetoamailslot (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hFile == INVALID_HANDLE_VALUE) { return FALSE; } fResult= WriteFile(hFile, lpszMessage.GetBuffer( 10 ), (DWORD)lstrlen(lpszMessage) + 1 , // includeterminatingnull & cbWritten, (LPOVERLAPPED)NULL); if ( ! fResult) { return FALSE; } fResult= CloseHandle(hFile); if ( ! fResult) { return FALSE; } return TRUE; }