第三章 邮槽

Windows(Windows CE除外)提供了一种简单的单向的进程间通信(InterProcess Communication,IPC)机制,叫做邮槽,通过邮槽,客户端
进程可将消息传送或广播给一个或多个服务器进程。邮槽在同一台计算机的不同进程间,或在跨越整个网络的不同计算机的进程间,协助进行
消息的传输。用邮槽开发应用程序是非常简单的,不要求对TCP/IP或IPX这样的基层网络传输协议有深入了解。由于邮槽是围绕一个广播通信
体系设计的,所以不能实现可靠传输,只能应用在那种对数据传输的可靠性要求不高的地方。邮槽最大的缺点是,只允许从客户端到服务器建
立一种不可靠的单向数据通信。

邮槽是围绕Windows文件系统接口设计出来的。客户机和服务器应用需要使用标准的Win32文件系统I/O(比如ReadFile和WriteFile等),以便
在邮槽上收发数据,同时利用Win32文件系统的命名规则。邮槽必须依赖 Windows重定向器,通过一个“邮槽文件系统”(Mailslot File
System, MSFS),来创建及标识邮槽。

邮槽的名字:
对邮槽进行标识时,需遵守下述命名规则:
\\server\Mailslot[\path]\name
其中,\\server是服务器,服务器字串部分可表示成一个小数点(.)、一个星号(*)、一个域名或者一个真正的服务器名字;\Mailslot是固
定字符串,用于告诉系统这个文件名从属于MSFS;[\path]\name是允许应用程序独一无二地定义及标识一个邮槽名,[\path]可以指定多级目录。

 邮槽名字类型
--------------------------------------------------------------------------------------------------------------------------
名字格式    说 明
--------------------------------------------------------------------------------------------------------------------------
\\.\mailslot\name   标定同一台机器上的一个本地邮槽
\\servername\mailslot\name  标定名为servername的一个远程邮槽服务器
\\domainname\mailslot\name  标定在指定的domain(域)内,使用特定name(名字)的所有邮槽
\\*\mailslot\name   标定系统主域内,标定特定name(名字)的所有邮槽
--------------------------------------------------------------------------------------------------------------------------

由于邮槽要依赖Windows文件系统服务在网上来创建和传输数据,所以接口是“与协议无关”的,应用程序不必关心基层网络传送协议的细节。
邮槽通过一个网络与计算机进行远程通信时,Windows文件系统服务需要依赖Windows重定向器,使用“服务器消息块”(SMB)协议,将数据从
客户机传给服务器。消息通常是通过“无连接”传输方式来发送的,但亦可强迫 Windows重定向器在Windows NT和Windows 2000上使用“面向
连接”的传输方式。至于具体采用哪种方式,要由消息的长度决定。

消息的长度:
邮槽通常用“数据报”(Datagram)在网络上传递消息,但在Windows NT和Windows 2000中,假如消息的长度超过426个字节,就会在一个SMB
会话之上,通过一种“面向连接”的协议进行传输,而不再采用无连接的“数据报”形式。然而,此时再也不能将一条消息从客户机广播给多
个服务器。因为对于“面向连接”的传输来说,它必然是一种“一对一”通信。而且,在使用“面向连接”方式下,邮槽接口不能保证一条消
息肯定能够写入一个邮槽。例如,假如从客户机向服务器送出一条较大的消息,但指定的服务器在网络中并不存在,那么邮槽接口不会告诉客
户机应用,数据到服务器的投递已经失败。

 邮槽消息的长度限制
--------------------------------------------------------------------------------------------------
传输方向    通过数据报进行“无连接”传输   进行“面向连接”的传输
--------------------------------------------------------------------------------------------------
Windows 95或Windows 98   消息长度高达64KB    不支持
-> Windows 95或Windows 98
--------------------------------------------------------------------------------------------------
Windows NT或Windows 2000  消息长度必须为424字节或以下   消息必须大于426个字节
-> Windows NT或Windows 2000
--------------------------------------------------------------------------------------------------
Windows NT或Windows 2000  消息长度必须为424字节或以下   不支持
-> Windows 95或Windows 98
--------------------------------------------------------------------------------------------------
Windows 95或Windows 98   消息长度必须为424字节或以下,   不支持
-> Windows NT或Windows 2000  多余的字节会被截去
--------------------------------------------------------------------------------------------------

由于Windows NT和Windows 2000重定向器不能收发长度正好为425或426字节的一条数据报消息,这种情况下Windows重定向器会在消息发给目
标服务器之前,将其长度截短为424字节。要想保证各种Windows平台之间能够完全正常地通信,强烈建议将消息长度限制在424字节或者更短。
如果进行面向连接的传输,可考虑使用命名管道,而不是简单的邮槽。

邮槽编程:
邮槽编程接口需要winbase.h头文件(该头文件已经包含在windows.h中了)和kernel32.lib库文件。开发邮槽客户机和服务器应用时,所有
Win32 API函数(CreateFile和CreateMailslot除外)在调用失败的情况下,都会返回0值。CreateFile和CreateMailslot这两个API在调用失败
时返回INVALID_HANDLE_VALUE(无效句柄值)。若这些API函数调用失败,应用程序应随即调用GetLastError函数,来接收与此次失败有关的
特殊信息。至于所有错误代码的一个完整列表可直接查询Winerror.h头文件。

邮槽建立起一个简单的客户机/服务器设计体系。在这个体系中,数据只能从客户机传到服务器,数据通信是单向进行的。服务器进程的职责
是创建一个邮槽,而且是能从邮槽读取数据的唯一一个进程。邮槽客户机进程则负责打开邮槽的“实例”,该进程是能够向其中写入数据的唯
一一个进程。

1、邮槽服务器:
步骤如下:
1)调用CreateMailslot API函数创建一个邮件,并返回邮槽句柄。
2)调用ReadFile API函数,并使用步骤1)得到的句柄,从任何客户端接收数据。
3)调用CloseHandle API函数,关闭邮槽。

CreateMailslot的定义如下:
HANDLE CreateMailslot(
 LPCTSTR lpName, //邮槽的名字,必须是\\.\mailslot[\path]\name,服务器名字必须是小数点,因为服务器只能在本地主机上创建邮槽
 DWORD nMaxMessageSize, //可写入邮槽的一条消息的最大长度(以字节为单位),假如客户机写入的字节数多于nMaxMessageSize的设置,服务器便不会接收这条消息。若将它的值设为0,服务器便会接收任意长度的消息。
 DWORD lReadTimeout, //以毫秒为单位,指定了读操作需要等候进入消息的时间。若将它的值设为MAILSLOT_WAIT_FOREVER,那么在进入的数据可以读取之前,读操作便会无限期地等待下去。若设为0,读操作就会立即返回。
 LPSECURITY_ATTRIBUTES lpSecurityAttributes //决定了为邮槽施加的访问控制权限。唯一能够施加的安全措施是针对本地I/O进行的——客户机试图将服务器的名字设为小数点(.)来打开一个邮槽这种情况。要想绕过这种安全机制,客户机可指定服务器的实际名字,而不是一个小数点,亦即相当于发出一个远程I/O调用。
);

ReadFile的定义如下:
BOOL ReadFile(
 HANDLE hFile,
 LPVOID lpBuffer,
 DWORD nNumberOfBytesToRead,
 DWORD lpNumberOfBytesRead,
 LPOVERLAPPED lpOverlapped
);
注意:用来读取数据的缓冲区的大小应该比来自CreateMailslot API调用的nMaxMessageSize参数的设置大。此外,缓冲区应该大于邮槽上的
进入消息;如果不够大,ReadFile调用便会失败,并返回一个ERROR_INSUFFICIENT_BUFFER错误。利用lpOverlapped参数,可以通过异步方式
进行数据的读取。该参数采用的是Win32重叠I/O机制,在默认情况下,ReadFile操作会处于暂停状态,直到有数据可以读入为止。重叠I/O只
能在Windows NT和Windows 2000上完成;如果使用的操作系统是 Windows 95或Windows 98,那么应将该参数设为NULL。

以下是服务器的例子:
/
#include <windows.h>
#include <stdio.h>

void main(void)
{
 HANDLE hMailslot;
 char buffer[256];
 DWORD dwNumOfBytesRead;
 if((hMailslot=CreateMailslot("\\\\.\\mailslot\\Myslot",0,MAILSLOT_WAIT_FOREVER,NULL))==INVALID_HANDLE_VALUE)
 {
  printf("ERROR: CreateMailslot: %d\n",GetLastError());
  return ;
 }
 while(ReadFile(hMailslot,buffer,256,&dwNumOfBytesRead,NULL)!=0)
 {
  buff[dwNumOfBytesRead]=0;
  printf("String: %s\nLength: %d\n",buffer,dwNumOfBytesRead);
 }
}
/

2、邮槽客户机:
步骤如下:
1)调用CreateFile API函数,打开邮槽并返回邮槽的引用句柄。
2)调用WriteFile API函数,向邮槽写入数据。
3)完成数据写入后,调用CloseHandle API函数关闭打开了的邮槽句柄。

CreateFile的定义如下:
HANDLE CreateFile(
 LPCTSTR lpFileName, //邮槽的名字
 DWORD dwDesiredAccess, //必须设为GENERIC_WRITE,因为客户机只能向邮槽写入数据。
 DWORD dwShareMode, //必须设为FILE_SHARE_READ,允许服务器在邮槽上打开和进行读操作。
 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //对于邮槽不会有什么效果,应将其设为NULL。
 DWORD dwCreationDisposition, //应设为OPEN_EXISTING,若一台机器既是客户机也是服务器,这一设置便显得尤其重要—如果服务器没有创建邮槽,CreateFile的调用便会失败。如果服务器在远程工作,那么该参数就没有意义了。
 DWORD dwFlagsAndAttributes, //应设成FILE_ATTRIBUTE_NORMAL
 HANDLE hTemplateFile //应设成NULL
);

WriteFile的定义如下:
BOOL WriteFile(
 HANDLE hFile,
 LPCVOID lpBuffer,
 DWORD nNumberOfBytesToWrite,
 LPDWORD lpNumberOfBytesWritten,
 LPOVERLAPPED lpOverlapped
);
注意:一条消息的最大长度为64KB。 如果是用一个域或星号(*)格式来打开邮槽的,那么在 Windows NT和Windows 2000中,消息的长度限制
在424字节之内;而在Windows 95和Windows 98中,限制在64KB之内。如客户机试图发送的消息超出了这一长度限制,WriteFile函数会便失败,
而且GetLastError函数会返回ERROR_BAD_NETPATH错误。

下面是邮槽客户机的例子:
//
#include <window.h>
#include <stdio.h>

void main(int argc, char *argv[])
{
 HANDLE hMailslot;
 DWORD dwBytesWritten;
 char ServerName[256];
 if(argc<2)
 {
  printf("Usage: client <server name>\n");
  return ;
 }
 sprintf(ServerName,"\\\\%s\\mailslot\\Myslot",argv[1]);
 if((hMailslot=CreateFile(ServerName,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL))==INVALID_HANDLE_VALUE)
 {
  printf("ERROR: CreateFile: %d\n",GetLastError());
  return;
 }
 if(WriteFile(hMailslot,"This is a test",14,&dwBytesWritten,NULL)==0)
 {
  printf("ERROR: WriteFile: %d\n",GetLastError());
  return;
 }
 printf("Wrote %d bytes\n",dwBytesWritten);
 CloseHandle(hMailslot);
}
//


其他邮槽API:
对邮槽服务器应用来说,它可使用另外两个API函数同邮槽打交道:GetMailslotInfo和SetMailslotInfo。其中,一旦邮槽上有消息可以传递,
GetMailslotInfo函数便可负责获取消息的长度信息等信息。利用这个函数,程序可针对长度不定的进入消息,动态地调节其缓冲区。
GetMailslotInfo函数亦可用来对进入数据进行“轮询”。函数的定义如下:
BOOL GetMailslotInfo(
 HANDLE hMailslot, //由CreateMailslot函数返回的邮槽句柄
 LPDWORD lpMaxMessageSize, //能够写入邮槽的消息的最大长度(单位为字节)
 LPDWORD lpNextSize, //下一条消息的长度,如果没有下一条消息,则返回MAILSLOT_NO_MESSAGE,利用这个参数,服务器可以在邮槽上轮询是否有消息进入,防止在应用程序在ReadFile函数调用中冻结了。但这种方式也不是种好的解决方法,建议使用Win32重叠I/O
 LPDWORD lpMessageCount, //等候接收的消息的总量
 LPDWORD lpReadTimeout //读操作的超时时间。
);

SetMailslotInfo函数用于设置一个邮槽的读操作的超时值,定义如下:
BOOL SetMailslot(
 HANDLE hMailslot,
 DWORD lReadTimeout
);

Windows 95和98平台的限制:
1)邮槽名字有8.3字符格式的限制,如果邮槽名字的长度超过这个限制,就会被自动截断,但函数的调用依然成功,所以,为了兼容这两种平
台,邮槽名字要限制在8个字符或更短。
2)如果邮槽服务器的ReadFile函数在等待读取数据,而服务器应用中止运行的话,则应用程序会被永远挂起,只能重启操作系统。为了解决
这个问题,必须将邮槽服务代码写在一个线程中。
3)如果在调用CreateMailslot时,指定一个大于0的超时值,那么一旦超时了,ReadFile函数就会造成内在的泄漏,同时返回FALSE,经过多
调用ReadFile之后,系统就会变得极不稳定,以后超时的ReadFile调用就会开始返回TRUE。为了解决这个问题,要将超时值设为0或
MAILSLOT_WAIT_FOREVER,禁用超时机制。
4)在没有安装一个网络客户机组件的情况下,调用GetMailslotInfo函数时,lpNextSize参数会返回一个不正确的值(通常有百万之大,或负数),
如果再次调用该函数,则往往会恢复正常,返回正确的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值