邮槽
邮槽是Windows系统提供的一种单向进程间的通信机制。对于相对简短的地坪率信息发送,使用邮槽通常比命名管道或者Unix域套接字更简单
使用邮槽通信的进程分为服务端和客户端。邮槽由服务端创建,在创建时需要指定邮槽名,创建后服务端得到邮槽的句柄。在邮槽创建后,客户端可以通过邮槽名打开邮槽,在获得句柄后可以向邮槽写入消息。
邮箱槽通信时单向的,只有服务端才能从邮箱槽中读取消息,客户端只能写入消息。消息先入先出。客户端先写入的消息在服务端先被读取。
通过邮槽通信的数据可以是任意格式的,但是一条消息不能大于424字节。
邮槽除了在本机内进行进程间的通信外,在主机间也可以通信。但是在主机间进行邮槽通信,数据通过网络传播时使用的是数据协议(UDP),所以是一种不可靠的通信。通过网络进行邮槽通信时,客户端必须知道服务端的主机名或域名。
过程
1.服务端通过CreateMailslot创建一个邮槽
2.客户端通过CreateFile与邮槽连接
3.客户端通过WritrFile写入数据
4.服务端通过ReadFile读数据
邮槽通信常用的API接口
CreateMailslot函数
HANDLE CreateMailslot(
[in] LPCWSTR lpName,
[in] DWORD nMaxMessageSize,
[in] DWORD lReadTimeout,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
- 参数lpName
指定邮槽的名称
邮槽的名称:
\\\\.\\mailslot\\[path\\]name–> 本机
\\\\DomainName\\[path\\]name–> 网络域名
\\\\ComputerName\\[path\\]name–> 网络计算机名
\\\\*\\mailslot\\[path\\]name –> 广播
此案例将利用邮槽来实现两个进程间的通信,选择第一个名称
- 参数nMaxMessageSize
可以写入邮槽的单个消息的最大大小(字节,0表示可以是任意大小
- 参数IReadTimeout
可以等待消息写入邮槽的事件,0表示没有消息立即返回;MAILSLOT_WAIT_FOREVER表示永远等待
函数成功返回邮件槽的句柄,失败则返回INVALID_HANDLE_VALUE。
CreateMailslotW function (winbase.h) - Win32 apps |微软学习 (microsoft.com)
ReadFile函数
BOOL ReadFile(
[in] HANDLE hFile,
[out] LPVOID lpBuffer,
[in] DWORD nNumberOfBytesToRead,
[out, optional] LPDWORD lpNumberOfBytesRead,
[in, out, optional] LPOVERLAPPED lpOverlapped
);
- 参数hFile
设备句柄
- 参数lpBuffer
接收数据缓冲区的指针
- 参数nNumberOfBytesToRead
读取的最大字节数
- 参数lpNumberOfBytesRead
指向变量的指针,用来接收使用同步hFile参数读取的字节数。
- 参数lpOverlapped
重叠结构的指针,使用FILE_OVERLAPPED打开hFile参数,则为必须,否则可为NNULL
ReadFile function (fileapi.h) - Win32 apps |微软学习 (microsoft.com)
CreateFile
HANDLE CreateFile(
[in] LPCWSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
- 参数lpFileName
要创建或打开的文件或设备的名称。这里填入邮槽名
- 参数dwDesiredAccess
对文件或设备的访问方式,可以分为读、写、两者或两者都不是
- 参数dwShareMode
请求共享模式,可以读取、写入、两者、删除、所有这些或 无。
取值 | 含义 |
---|---|
0 0x00000000 | 防止其他进程在请求删除、读取或写入访问权限时打开文件或设备。 |
FILE_SHARE_DELETE 0x00000004 | 对文件或设备启用后续打开操作以请求删除访问权限。 否则,如果其他进程请求删除访问权限,则无法打开文件或设备。 如果未指定此标志,但已打开文件或设备以进行删除访问,则函数 失败。
注意删除访问权限允许删除和重命名操作。
|
FILE_SHARE_READ 0x00000001 | 启用对文件或设备的后续打开操作以请求读取访问权限。 否则,如果其他进程请求读取访问权限,则无法打开文件或设备。 如果未指定此标志,但文件或设备已打开以进行读取访问,则函数 失败。 |
FILE_SHARE_WRITE 0x00000002 | 允许对文件或设备执行后续打开操作以请求写入访问权限。 否则,如果其他进程请求写入访问权限,则无法打开文件或设备。 如果未指定此标志,但文件或设备已打开以进行写入访问或具有文件映射 使用写入访问权限时,函数将失败。 |
- 参数lpSecurityAttributes
安全描述符,为NULL表示任何子进程都不能继承CreateFile返回的句柄。
- 参数dwCreationnDisposition
对存在或不存在的文件或设备执行的操作。
对于文件以外的设备,此参数通常设置为OPEN_EXISTING
- 参数dwFlagAndAttrubutes
文件或设备属性和标志,FILE_ATTRIBUTE_NORMAL是最多的 文件的通用默认值。
- 参数hTemplateFile
此参数可以为NULL。
CreateFileW 函数 (fileapi.h) - Win32 apps |微软学习 (microsoft.com)
BOOL WriteFile(
[in] HANDLE hFile,
[in] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[out, optional] LPDWORD lpNumberOfBytesWritten,
[in, out, optional] LPOVERLAPPED lpOverlapped
);
- 参数hFile
文件或设备句柄
- 参数lpBuffer
写入数据的缓冲区指针
- 参数nNumberOfBytesToWrote
写入文件或设备的字节数
- 参数lpNumberOfBytesWritten
指向变量的指针,该变量接收使用同步hFile参数时写入的字节数。
- 参数lpOverlapped
指向重叠结构的指针是 如果使用FILE_FLAG_OVERLAPPED 打开hFile参数,则为必需,否则此参数可以为NULL。
如果函数成功,则返回值为非零值 (TRUE),如果函数失败或异步完成,则返回值为零(FALSE)。
WriteFile function (fileapi.h) - Win32 apps |微软学习 (microsoft.com)
Demo示例
两个基于单个对话框的MFC应用
在菜单栏中点击“服务端”,响应函数创建一个邮槽,然后开始永远等待消息的到来,将接收到的消息通过提示框显示出来
在菜单栏中点击“发送数据”,响应函数会与邮槽连接,并向邮槽写入数据
服务端
void CChildView::OnSlot()
{
//邮槽名 "\\.\mailslot\Mymailslot"
//1.创建一个邮槽
LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
HANDLE hSlot;
hSlot = CreateMailslot(szSlotName,
0, //最大消息大小的限制
MAILSLOT_WAIT_FOREVER, //无超时操作
(LPSECURITY_ATTRIBUTES)NULL //默认安全
);
//判断创建的邮槽是否有效
if (hSlot == INVALID_HANDLE_VALUE) {
//如果创建的邮槽是一个无效的句柄
TRACE("CreateMailslot failed with %d\n", GetLastError());
return;
}
//2.读数据
char szBuf[100] = { 0 };
DWORD dwRead;
//阻塞在这里
if (!ReadFile(hSlot, szBuf, 100, &dwRead, NULL)) {
MessageBox(_T("读取失败"));
CloseHandle(hSlot);
//return;
}
else {
MessageBox((CStringW)szBuf);
}
}
发送数据
void CChildView::OnSend()
{
LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
//创建一个文件句柄
HANDLE hMailSlot = CreateFile(szSlotName,
FILE_GENERIC_WRITE,//权限
FILE_SHARE_READ,//共享模式
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hMailSlot == INVALID_HANDLE_VALUE) {
TRACE("CreateFile failed with %d\n", GetLastError());
return;
}
//写入数据
char szBuf[] = "北极熊猫很handsome";
DWORD dwWrite;
if (!WriteFile(hMailSlot, szBuf, strlen(szBuf) + 1, &dwWrite, NULL)) {
MessageBox(_T("写入数据失败"));
CloseHandle(hMailSlot);
return;
}
CloseHandle(hMailSlot);
}
执行结果: