邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠UDP数据传输协议。使用邮槽通信的进程分为客户端和服务端,邮槽由服务端创建,创建后,客户端可以通过邮槽名打开邮槽,在获得邮槽句柄后可以向邮槽写入消息。邮槽通信是单向的,只有服务端能从邮槽中读取消息,而客户端只能写入消息。消息是先进先出的。
通过邮槽通信的数据可以是任意格式的,但为了保证邮槽在各种Windows平台下都能够正常工作,邮槽通信一条消息的长度不能大于424字节。邮槽除了在本机上进行进程间通信外,还可以在主机之间进行通信。
实际上一个邮槽是驻留在内存中的一个Windows临时虚拟文件,利用Windows标准文件函数可以对邮槽写入或读取消息,但它不同于磁盘文件的地方是:当邮槽句柄被关闭后,邮槽中的消息将被全部删除。
因此,邮槽工作方式有三大特定:1)单向通信;2)广播消息;3)数据报传输。
1)邮槽的命名:
本机上邮槽命名格式://./mailslot/[path/]name;
例如://./mailslot/win/asce_comment;
不同主机间命名格式://DomainName/mailslot/[path/]name;
//ComputerName/mailslot/[path/]name;
也可以使用通配符,以进行广播://*/mailslot/[path/]name;
格式的说明:前两个反斜杠之后的字符表示服务器所在机器的名称,圆点表示是本地主机;“mailslot”是硬编码的,这几个字符不能改变,但大小写无所谓。“[path/]name”当然就是邮槽名字了。
2)关键的API
CreateMailslot,创建一个邮槽对象:
HANDLE WINAPI CreateMailslot(
__in LPCTSTR lpName, //邮槽名
__in DWORD nMaxMessageSize, //单一消息最大长度,为了可以发送任意大小的消息,
//一般将该参数设置为0
__in DWORD lReadTimeout, //读超时的时间:0(如果没有消息时立即返回);
//MAILSLOT_WAIT_FOREVER(直到读到消息才返回)
__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes //安全属性
);
例子如下:
#include <windows.h>
#include <stdio.h>
HANDLE hSlot;
LPTSTR Slot = TEXT("////.//mailslot//sample_mailslot");
BOOL WINAPI MakeSlot(LPTSTR lpszSlotName)
{
hSlot = CreateMailslot(lpszSlotName,
0, // no maximum message size
MAILSLOT_WAIT_FOREVER, // no time-out for operations
(LPSECURITY_ATTRIBUTES) NULL); // default security
if (hSlot == INVALID_HANDLE_VALUE)
{
printf("CreateMailslot failed with %d/n", GetLastError());
return FALSE;
}
else printf("Mailslot created successfully./n");
return TRUE;
}
void main()
{
MakeSlot(Slot);
}
GetMailslotInfo,获取指定邮槽的相关信息:
BOOL WINAPI GetMailslotInfo(
__in HANDLE hMailslot, //邮槽的句柄
__out_opt LPDWORD lpMaxMessageSize, //返回消息的最大长度
__out_opt LPDWORD lpNextSize, //返回下一条消息的长度
__out_opt LPDWORD lpMessageCount, //返回消息的数量
__out_opt LPDWORD lpReadTimeout //返回读超时时间
);
例子如下:(这个例子同时是一个完整的邮槽服务端)
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
HANDLE hSlot;
LPTSTR SlotName = TEXT("////.//mailslot//sample_mailslot");
BOOL ReadSlot()
{
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPTSTR lpszBuffer;
TCHAR achID[80];
DWORD cAllMessages;
HANDLE hEvent;
OVERLAPPED ov;
cbMessage = cMessage = cbRead = 0;
hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("ExampleSlot"));
if( NULL == hEvent )
return FALSE;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = hEvent;
fResult = GetMailslotInfo( hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed with %d./n", GetLastError());
return FALSE;
}
if (cbMessage == MAILSLOT_NO_MESSAGE)
{
printf("Waiting for a message.../n");
return TRUE;
}
cAllMessages = cMessage;
while (cMessage != 0) // retrieve all messages
{
// Create a message-number string.
StringCchPrintf((LPTSTR) achID,
80,
TEXT("/nMessage #%d of %d/n"),
cAllMessages - cMessage + 1,
cAllMessages);
// Allocate memory for the message.
lpszBuffer = (LPTSTR) GlobalAlloc(GPTR,
lstrlen((LPTSTR) achID)*sizeof(TCHAR) + cbMessage);
if( NULL == lpszBuffer )
return FALSE;
lpszBuffer[0] = '/0';
fResult = ReadFile(hSlot,
lpszBuffer,
cbMessage,
&cbRead,
&ov);
if (!fResult)
{
printf("ReadFile failed with %d./n", GetLastError());
GlobalFree((HGLOBAL) lpszBuffer);
return FALSE;
}
// Concatenate the message and the message-number string.
StringCbCat(lpszBuffer,
lstrlen((LPTSTR) achID)*sizeof(TCHAR)+cbMessage,
(LPTSTR) achID);
// Display the message.
_tprintf(TEXT("Contents of the mailslot: %s/n"), lpszBuffer);
GlobalFree((HGLOBAL) lpszBuffer);
fResult = GetMailslotInfo(hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed (%d)/n", GetLastError());
return FALSE;
}
}
CloseHandle(hEvent);
return TRUE;
}
BOOL WINAPI MakeSlot(LPTSTR lpszSlotName)
{
hSlot = CreateMailslot(lpszSlotName,
0, // no maximum message size
MAILSLOT_WAIT_FOREVER, // no time-out for operations
(LPSECURITY_ATTRIBUTES) NULL); // default security
if (hSlot == INVALID_HANDLE_VALUE)
{
printf("CreateMailslot failed with %d/n", GetLastError());
return FALSE;
}
return TRUE;
}
void main()
{
MakeSlot(SlotName);
while(TRUE)
{
ReadSlot();
Sleep(3000);
}
}
SetMailslotInfo,修改已创建邮槽读操作的超时时间:
BOOL WINAPI SetMailslotInfo(
__in HANDLE hMailslot, //邮槽句柄
__in DWORD lReadTimeout //新的读超时时间
);
邮槽的客户端代码如下:
#include <windows.h>
#include <stdio.h>
LPTSTR SlotName = TEXT("////.//mailslot//sample_mailslot");
BOOL WriteSlot(HANDLE hSlot, LPTSTR lpszMessage)
{
BOOL fResult;
DWORD cbWritten;
fResult = WriteFile(hSlot,
lpszMessage,
(DWORD) (lstrlen(lpszMessage)+1)*sizeof(TCHAR),
&cbWritten,
(LPOVERLAPPED) NULL);
if (!fResult)
{
printf("WriteFile failed with %d./n", GetLastError());
return FALSE;
}
printf("Slot written to successfully./n");
return TRUE;
}
int main()
{
HANDLE hFile;
hFile = CreateFile(SlotName,
GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with %d./n", GetLastError());
return FALSE;
}
WriteSlot(hFile, TEXT("Message one for mailslot."));
WriteSlot(hFile, TEXT("Message two for mailslot."));
Sleep(5000);
WriteSlot(hFile, TEXT("Message three for mailslot."));
CloseHandle(hFile);
return TRUE;
}
由于邮槽是基于广播通信的,所以邮槽可以实现一对多的单向通信,例如,我们可以利用邮槽编写一个网络会议的通知系统。在每个被通知人电脑上安装服务端,通知人电脑上安装客户端即可。