邮槽

3.1.1 邮槽的名字
对邮槽进行标识时,需遵守下述命名规则:

\ \ s e r v e r \ M a i l s l o t \ [ p a t h ] n a m e
请将上述字串分为三段来看: \ \ s e r v e r、\ M a i l s l o t和\ [ p a t h ] n a m e 。第一部分 \ \ s e r v e r 对应于
服务器的名字,我们要在上面创建邮槽,并在在上面运行服务器程序。第二部分 \ M a i l s l o t是一
个“硬编码”的固定字串,用于告诉系统这个文件名从属于 M S F S。而第三部分 \ [ p a t h ] n a m e 则
允许应用程序独一无二地定义及标识一个邮槽名。其中, “p a t h”代表路径,可指定多级目录。
举个例子来说,对一个邮槽进行标识时,下面这些形式的名字都是合法的(注意 M a i l s l o t不得
变化,必须原文照输,亦即所谓的“硬编码” ) :
\ \ O r e o \ M a i l s l o t \ M y m a i l s l o t
\ \ Te s t s e r v e r \ M a i l s l o t \ C o o l d i r e c t o r y \ F u n t e s t \ A n o t h e r m a i l s l o t
\ \ . \ M a i l s l o t \ E a s y m a i l s l o t
\ \ * \ M a i l s l o t \ M y s l o t
服务器字串部分可表示成一个小数点( .) 、一个星号(*) 、一个域名或者一个真正的服务
器名字。所谓“域” ,其实就是一系列工作站和服务器的组合,它们共用一个相同的组名。本
章稍后,在讲到一个简单的客户机的细节时,还会就邮槽名字作深入探讨。
由于邮槽要依赖 Wi n d o w s 文件系统服务在网上来创建和传输数据,所以接口是“与协议
无关”的。编制自己的应用程序时,便不必关心基层网络传送协议的细节,不必知道它在一
个网络中如何在进程之间建立通信。邮槽通过一个网络与计算机进行远程通信时, Wi n d o w s
文件系统服务需要依赖 Wi n d o w s 重定向器,使用“服务器消息块” (S M B)协议,将数据从客
户机传给服务器。消息通常是通过“无连接”传输方式来发送的,但亦可强迫 Wi n d o w s 重定
向器在Windows NT和Windows 2000上使用“面向连接”的传输方式。至于具体采用哪种方式,
要由消息的长度决定。
3.1.2 消息的长度
邮槽通常用“数据报” (D a t a g r a m)在网络上传递消息。数据报实际是一些小数据包,只
不过要以“无连接”的形式,通过网络进行传输。 “无连接”意味着每个数据包在发给接收者
之后,不要求对方提供包的收到确认信息。显然,这是一种“不可靠”的数据传输,因为无
法保证消息肯定能正确送达。然而,通过无连接传输,我们确实可将消息从一个客户机广播
给多个服务器。当然,上述情况也有例外。在 Windows NT和Windows 2000中,假如消息的长
度超过4 2 4个字节,便会发生这种例外。
在Windows NT和Windows 2000中,若消息长度超过 4 2 6个字节,便必须在一个 S M B会话
之上,通过一种“面向连接”的协议进行传输,而不再采用无连接的“数据报”形式。这样
一来,在消息较大的情况下,便可保证它们的稳定、高效传输。然而,此时再也不能将一条
消息从客户机广播给多个服务器。对于“面向连接”的传输来说,它必然是一种“一对一”
通信:一个客户机对一个服务器!在不同的进程之间,使用“面向连接”的传输方式,往往
可保证数据传输的可靠性。但在 Windows NT和Windows 2000中,邮槽接口却不能保证一条消
息肯定能够写入一个邮槽。举个例子来说,假如从客户机向服务器送出一条较大的消息,但
指定的服务器在网络中并不存在,那么邮槽接口不会告诉你的客户机应用,数据到服务器的
投递已经失败。由于Windows NT和Windows 2000要根据消息的长度,来更改它们的传输方法,
所以假定一台机器运行的是 Windows NT或Windows 2000,而另一台机器运行的是 Windows 95

或者Windows 98,便会面临两种系统在沟通上的困难。
Windows 95和Windows 98只以“数据报”的形式来传送消息,无论消息长度如何。假如
Windows 95 或Windows 98 客户机试图将一条长度超过 4 2 4字节的消息传给 Windows NT 或
Windows 2000服务器,Windows NT和Windows 2000便会只接受头4 2 4个字节,剩下的数据会
被无情地“砍掉” 。换言之,Windows NT和Windows 2000要求更大的消息通过一个“面向连
接”的S M B会话进行传输。
消息从Windows NT或Windows 2000客户机传向Windows 95或Windows 98服务器时,也
存在着类似的问题。请记住, Windows 95和Windows 98只通过数据报接收数据。由于对超过
4 2 6个字节的消息来说, Windows NT和Windows 2000不会通过数据报来传输数据,所以在这
种情况下, Windows 95和Windows 98不能从这些客户机收到消息。在表 3 - 1中,我们为大家详
细总结了这种消息长度上的限制。
注意 Windows CE已从表3-1中剔除出去了,因为它并未提供邮槽编程接口。另外要注
意的是,由于Windows NT和Windows 2000重定向器的限制,长度为4 2 5或4 2 6字节的消
息未在此表列出。
表3-1 邮槽消息的长度限制
传输方向 通过数据报进行“无连接”传输 进行“面向连接”的传输
Windows 95或Windows 98 消息长度高达 6 4 K B 不支持
- > Windows 95或Windows 98
Windows NT或Windows 2000 消息长度必须为4 2 4字节或以下 消息必须大于 4 2 6个字节
- > Windows NT或Windows 2000
Windows NT或Windows 2000 消息长度必须为4 2 4字节或以下 不支持
- > Windows 95或Windows 98
Windows 95或Windows 98 消息长度必须为4 2 4字节或以下; 不支持
- > Windows NT 或Windows 2000 多余的字节会被截去
对Windows NT和Windows 2000这两种网络操作系统来说,还有另一项限制值得我们注
意,因为它会对数据报数据的传输产生影响。 Windows NT 和Windows 2000 重定向器不能
收发长度正好为 4 2 5 或 4 2 6 字节的一条数据报消息。例如,假定我们从 Windows NT 或
Windows 2000客户机向 Windows 95、Windows 98、Windows NT或Windows 2000服务器发
送这样的一条消息, Windows NT重定向器会在消息发给目标服务器之前,将其长度截短为
4 2 4字节。
要想保证各种 Wi n d o w s 平台之间能够完全正常地通信,强烈建议将消息长度限制在 4 2 4字
节,或者更短。如果进行面向连接的传输,可考虑使用命名管道,而不是简单的邮槽。命名
管道将在第 4章讨论。

3.2.1 邮槽服务器的详情
若想实现一个邮槽,要求开发一个服务器应用,来负责邮槽的创建。下述步骤解释了如
何编写一个基本的服务器应用:
1) 用CreateMailslot API函数创建一个邮槽句柄。
2) 调用ReadFile API函数,并使用现成的邮槽句柄,从任何客户机接收数据。
3) 用C l o s e H a n d l e这个A P I函数,关闭邮槽句柄。
可以看出,要开发一个邮槽服务器程序,只需使用极少的 A P I 调用。服务器进程是用
C r e a t e M a i l s l o t这个A P I调用来创建邮槽的。定义如下:
其中,第一个参数 l p N a m e指定邮槽的名字。名字的格式如下:
\ \ . \ M a i l s l o t \ [ p a t h ] n a m e
要注意的是,服务器的名字用一个小数点来表示,亦即服务器就是本地机器。这样做是
很有必要的,因为我们不能在远程计算机上创建邮槽。在 l p N a m e 参数中,名字必须以一种独
一无二的形式表达。可将它设为一个独立的名字,也可以在它前面加上一个完整的目录路径。
n M a x M e s s a g e S i z e参数定义的是可写入邮槽的一条消息的最大长度(以字节为单位) 。假
如客户机写入的字节数多于 n M a x M e s s a g e S i z e的设置,服务器便不会接收这条消息。若将它的
值设为0,服务器便会接收任意长度的消息。
在一个邮槽上,读操作可以等待或不等待这两种模式进行,具体由 l R e a d Ti m e o u t参数决
定。它以毫秒为单位,指定了读操作需要等候进入消息的时间。若将它的值设为
M A I L S L O T _ WA I T _ F O R E V E R,那么在进入的数据可以读取之前,读操作便会无限期地等待
下去。若设为 0 ,读操作就会立即返回。本章稍后,还会就读操作的详情进行探讨。

l p S e c u r i t y A t t r i b u t e s参数决定了为邮槽施加的访问控制权限。在 Windows NT和Windows 2000
中,这个参数只实现了一部分,所以同时还应指定一个 n u l l(空) 参数。在邮槽上,唯一能
够施加的安全措施是针对本地 I / O 进行的—客户机试图将服务器的名字设为小数点( . ) ,以
打开一个邮槽。要想绕过这种安全机制,客户机可指定服务器的实际名字,而不是一个小数
点(.) ,亦即相当于发出一个远程 I / O调用。在 Windows NT和Windows 2000中,并未针对远
程I / O而实现l p S e c u r i t y A t t r i b u t e s参数,因为假如每次发出一条消息时,都在客户机与服务器之
间建立一个授权会话,那么效率会显得十分低下。因此,邮槽仅一部分符合标准文件系统采
用的Windows NT和Windows 2000安全模型。结果便是,网络中的任何邮槽客户机都可将数据
发给服务器。
用一个有效的句柄创建了邮槽之后,便可开始数据的实际读取。服务器是唯一能从邮槽
读入数据的进程。服务器应使用 R e a d F i l e这个Wi n 3 2函数,来进行数据的读取。对 R e a d F i l e的
定义如下:
C r e a t e M a i l s l o t会返回一个句柄 h F i l e。l p B u ff e r和n N u m b e r O f B y t e s To R e a d参数决定了可从
邮槽读入多少数据。特别值得注意的是,这个缓冲区的大小应该比来自 CreateMailslot API调
用的n M a x M e s s a g e S i z e参数的设置大。此外, 缓冲区应该大于邮槽上的进入消息;如果不够大,
R e a d F i l e 调用便会失败,并返回一个 E R R O R _ I N S U F F I C I E N T _ B U F F E R 错误。
l p N u m b e r O f B y t e s R e a d 参数用于在 R e a d F i l e操作完成后,报告读入的实际字节数量。
利用 l p O v e r l a p p e d 参数,我们可以通过异步方式,进行数据的读取。该参数采用的是
Wi n 3 2 重叠I / O机制,第 4章还会对这个问题进行深入的讲解。在默认情况下, R e a d F i l e操作会
处于暂停状态,直到有数据可以读入为止。重叠 I / O只能在 Windows NT和Windows 2000上完
成;如果使用的操作系统是 Windows 95或Windows 98,那么应将该参数设为 N U L L。在程序
清单3 - 1中,我们进一步阐释了如何编写一个简单的邮槽服务器应用。
程序清单3-1

#include <Windows.h>
#include <stdio.h>

int main()
{
char buffer[256]={0};
DWORD num;
int i;
HANDLE hMailslot;
hMailslot=CreateMailslot(TEXT("\\\\.\\Mailslot\\MailslotTest"),0,MAILSLOT_WAIT_FOREVER,NULL);
if(hMailslot==INVALID_HANDLE_VALUE)
{
printf("创建邮槽失败!");
return 0;
}

while(ReadFile(hMailslot,buffer,256,&num,NULL)!=0)
{
printf("%d-",num);
for(i=0;i<num;i++)
{
printf("%c",buffer[i]);
}
printf("\n");
}
CloseHandle(hMailslot);
}

3.2.2 邮槽客户机的详情
要想实现一个客户机,需要开发一个应用程序,对一个现有的邮槽进行引用和写入。下
述步骤解释了如何编写一个基本的客户机应用:
1) 使用C r e a t e F i l e这个A P I函数,针对想向其传送数据的邮槽,打开指向它的一个引用句
柄。
2) 调用Wr i t e F i l e 这个A P I函数,向邮槽写入数据。
3) 完成了数据的写入后,用 C l o s e H a n d l e这个A P I函数,关闭打开的邮槽句柄。
如前所述,采用一种“无连接”的形式,邮槽客户机同邮槽服务器通信。客户机打开指
向邮槽的一个引用句柄时,实际并不建立同邮槽服务器的一个连接。要想对一个邮槽进行引
用,需要使用C r e a t e F i l e这个A P I调用。对它的定义如下:
l p F i l e N a m e参数用于描述一个或多个邮槽,我们可用本章早些时候介绍的邮槽命名格式,
向其写入数据。在表 3 - 2中,我们更深入地总结了邮槽的命名规范。 d w D e s i r e d A c c e s s参数必须
设为 G E N E R I C _ W R I T E,因为客户机只能向服务器写入数据。 d w S h a r e M o d e参数必须设为
F I L E _ S H A R E _ R E A D,允许服务器在邮槽上打开和进行读操作。 l p S e c u r i t y A t t r i b u t e s 参数对于
邮槽不会有什么效果,应将其设为N U L L。 d w C r e a t i o n D i s p o s i t i o n标志应设为O P E N _ E X I S T I N G。
若一台机器既是客户机,也是服务器,这一设置便显得尤其重要—如果服务器没有创建邮
槽,对A P I函数C r e a t e F i l e的调用便会失败。如果服务器在远程工作,那么。 d w C r e a t i o n D i s p o s i t i o n

表3-2 邮槽名字类型
名字格式 说 明
\ \ . \ m a i l s l o t \ n a m e 标定同一台机器上的一个本地邮槽
\ \ s e r v e r n a m e \ m a i l s l o t \ n a m e 标定名为s e r v e r n a m e的一个远程邮槽服务器
\ \ d o m a i n n a m e \ m a i l s l o t \ n a m e 标定在指定的 d o m a i n(域)内,使用特定 n a m e(名字)的所有邮槽
\ \ * \ m a i l s l o t \ n a m e 标定系统主域内,标定特定 n a m e(名字)的所有邮槽
参数便没什么意义 d w F l a g s A n d A t t r i b u t e s 参数应定义成 F I L E _ AT T R I B U T E _ N O R M A L 。
h Te m p l a t e F i l e参数应设为N U L L。
成功创建一个句柄后,便可开始向邮槽写入数据。请记住,作为客户机,只能将数据写
入邮槽。这可以用 Wi n 3 2函数Wr i t e F i l e来做到,定义如下:
其中,h F i l e参数是由 C r e a t e F i l e返回的一个引用句柄。 l p B u ff e r和n N u m b e r O f B y t e s To Wr i t e
参数决定了有多少字节从客户机发向服务器。一条消息的最大长度为 6 4 K B。如果当初是用一
个域或星号( *)格式来创建邮槽句柄,那么在 Windows NT和Windows 2000中,消息的长度
限制在4 2 4字节之内;而在 Windows 95和Windows 98中,限制在6 4 K B之内。如客户机试图发
送的消息超出了这一长度限制, Wr i t e F i l e 函数会便失败,而且 G e t L a s t E r r o r函数会返回
E R R O R _ B A D _ N E T PAT H 错误。之所以会出现这一情况,是由于需要以广播数据报的形式,
把消息发送给网络中的所有服务器。 l p N u m b e r O f B y t e s Wr i t t e n参数返回的是当 Wr i t e F i l e操作完
成后,传给服务器的实际字节数量。
通过l p O v e r l a p p e d 参数,我们可采用异步形式,将数据写入一个邮槽。由于邮槽最大的特
点便是“无连接”的数据传输,所以 Wr i t e F i l e 函数不会在 I / O调用的时候暂停等候。在客户机
上,这个参数应设为 N U L L。在程序清单 3 - 2中,我们进一步阐述了如何编写一个简单的邮槽
客户端应用。
程序清单3-2 客户机邮槽实例

#include <Windows.h>
#include <stdio.h>

int main()
{
DWORD num;
HANDLE hMailslot;
hMailslot=CreateFile(TEXT("\\\\USER-20140518DI.\\Mailslot\\MailslotTest"),GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hMailslot==INVALID_HANDLE_VALUE)
{
printf("打开邮槽失败!\n");
return 0;
}

while(WriteFile(hMailslot,TEXT("jiangtaidi come in\n"),sizeof(TEXT("jiangtaidi come in\n")),&num,NULL));
printf("%d",num);
CloseHandle(hMailslot);
getchar();
return 0;

}

3.3 其他邮槽API
对邮槽服务器应用来说,它可使用另外两个 A P I函数同邮槽打交道: G e t M a i l s l o t I n f o和
S e t M a i l s l o t I n f o。其中,一旦邮槽上有消息可以传递, G e t M a i l s l o t I n f o函数便可负责获取消息
的长度信息。利用这个函数,程序可针对长度不定的进入消息,动态地调节其缓冲区。
G e t M a i l s l o t I n f o函数亦可用来对进入数据进行“轮询” 。对G e t M a i l s l o t I n f o函数的定义如下:
其中,h M a i l s l o t参数指定自 CreateMailslot API 调用返回的一个邮槽。 l p M a x M e s s a g e S i z e
参数设置可将多大的一条消息写入邮槽(以字节为单位) 。l p N e x t S i z e参数则以字节为单位,
指出下一条消息的长度。 G e t M a i l s l o t I n f o可能会返回一个 M A I L S L O T _ N O _ M E S S A G E值,指
出在邮槽之上,目前没有等待接收的消息。利用这个参数,服务器便可在邮槽上轮询(不断
地查询)是否有进入的消息,防止应用程序在 R e a d F i l e函数调用过程中“凝结” ,傻乎乎地一
直等候下去。然而,用这种方式对数据进行轮询并不是一种很好的编程习惯。因为应用程序
会连续不停地消耗宝贵的 C P U资源,以检查进入的数据—即使根本没有需要处理的消息。
这样一来,便会降低系统的总体性能。如果想防止 R e a d F i l e“凝结”或暂停执行,建立你使用
Wi n 3 2 的重叠 I / O 。l p M e s s a g e C o u n t 参数指定一个缓冲区,用于接收等候读入的消息的总量。
l p R e a d Ti m e o u t参数则指定了另一个缓冲区,它以毫秒为单位,返回了一次读操作最多能等候
多久的时间,让一条消息写入邮槽。
S e t M a i l s l o t I n f o函数用于设置一个邮槽的超时值。超过这个时间,读操作便不再等候进入
消息。因此,应用程序完全有能力将读操作从“凝结”状态转变成“非凝结”状态;或者相
反。对S e t M a i l s l o t I n f o的定义如下:
第3章计邮 槽计计61
下载
b M a i l s l o t参数指定一个自 CreateMailslot API调用返回的邮槽。 l R e a d Ti m e o u t 参数以毫秒
为单位,指定一次读操作等候一条消息写入邮槽的最长时间。若将其设为 0,在不存在消息的
前提下,读操作便会立即返回;若设为 M A I L S L O T _ WA I T _ F O R E V E R,读操作便会永远等待
下去。
3.4 平台和性能问题
在Windows 95(含O S R 2)和Windows 98(含第二版)这两种平台上,邮槽存在着一些必
须引起我们警惕的限制: “8 . 3字符名字限制” 、不能取消“凝结”的 I / O 请求;以及由于超时引
起的内存废弃。
3.4.1 8.3字符名字限制
Windows 95和Windows 98不声不响地将邮槽名字限制在 8 . 3字符格式的范围之内。这样一
来,有时候就会造成 Windows 95/98和Windows NT/2000之间的“不和” 。举个例子来说,假定
我们用\ \ . \ M a i l s l o t \ M y m a i l s l o t这个名字来创建或打开一个邮槽,Windows 95实际会以\ \ . \ M a i l s l o t \
M y m a i l s l 的形式,来创建及引用邮槽。尽管会发生名字被截短的现象, C r e a t e M a i l s l o t和
C r e a t e F i l e函数执行仍会成功完成。然而,假如一条消息从 Windows 2000发给Windows 95—
或者相反,便无法收到这条消息,因为邮槽的名字并不相符。假如客户机和服务器均在
Windows 95机器上运行,则不会出现这种问题—名字在客户机和服务器上都会被截短。要
想防范这种互不兼容的问题,一定要将邮槽的名字限制在 8个字符或者更短。
3.4.2 不能取消“凝结”的I/O请求
Windows 95和Windows 98在取消暂停或“凝结”的 I / O请求时,也会出现问题。邮槽服务
器用R e a d F i l e函数来接收数据。假如用 M A I L S L O T _ WA I T _ P O R E V E R标志创建了一个邮槽,
读请求便会一直等待下去,直到有数据可用为止。假如在一个 R e a d F i l e请求尚未完成的前提下,
服务器应用突然中止运行,应用程序便会被永远地“挂起” ,或者“凝结” 。要想取消,唯一
的办法就是重新启动 Wi n d o w s 。为解决这个问题,可让服务器在单独一个线程中,打开一个
句柄,令其指向自己的邮槽。并发送数据,以终止处于暂停状态的读操作。在程序清单 3 - 3中,

3.4.3 超时引起的内存废弃
对于Windows 95和Windows 98来说,值得注意的最后一个问题便是内存空间的“废弃” ,
或者说内存空间产生了“漏洞” 。在邮槽上使用超时设定时,便有可能出现这一现象。假如用
C r e a t e M a i l s l o t函数创建一个邮槽,同时将超时时间设为大于 0的一个值,那么一旦超过这一时
间, R e a d F i l e函数便会造成内存空间的废弃, 生成所谓的“内存漏洞” 。同时,函数会返回
FA L S E。经过多次R e a d F i l e函数调用之后,系统就会变得极不稳定,以后超时的 R e a d F i l e调用
会开始返回 T R U E。因此,系统不能再执行其他 M S - D O S程序。为解决这个问题,请将超时值
设为0或者M A I L S L O T _ WA I T _ F O R E V E R。这样便可禁止应用程序使用超时机制,从而避免
了实际产生的内存“漏洞” 。
在微软知识库中,描述了下述问题,以及它们存在的限制。这个知识库是完全公开的,
网上地址在 h t t p : / / s u p p o r t . m i c r o s o f t . c o m / s u p p o r t / s e a r c h。这里只对相关的主题进行简单介绍:
■ Q 1 3 9 7 1 5:R e a d F i l e为邮槽返回错误的“错误代码”
64计计第一部分附传统网络API
下载
假如服务器用 C r e a t e M a i l s l o t打开一个邮槽,指定一个超时,然后用 R e a d F i l e接收数据,
那么假如没有数据可用(可以读取) ,R e a d F i l e便会失败。此时用 G e t L a s t E r r o r函数会返回一个
错误代码5(拒绝访问) 。
■ Q 1 9 2 2 7 6:G e t M a i l s l o t I n f o返回不正确 l p N e x t S i z e 值
如果在 Windows 95 OEM Service Release 2 (O S R 2)或 Windows 98 中调用 A P I函数
G e t M a i l s l o t I n f o,同时没有安装一个网络客户机组件,那么对 l p N e x t S i z e 参数来说,便会通过
它接收到一个不确切的值(通常有百万之大) ,或者接收到一个负数。如再次调用该函数,则
往往会恢复正常,返回正确的值。
■ Q 1 7 0 5 8 1:在Wi n 9 5中创建的邮槽只允许 4 0 9 3个字节的消息
如调用Wr i t e F i l e这个A P I函数,将多于4 0 9 3个字节的数据写入已在 Windows 95工作站上创
建好的一个邮槽,操作便会失败。
■ Q 1 3 1 4 9 3:C r e a t e F i l e和邮槽的问题
在A P I函数C r e a t e F i l e的用户文档说明中,错误描述了在打开一个邮槽的客户机那一端时,
C r e a t e F i l e可能返回的值。

转载于:https://www.cnblogs.com/jgogo/p/3834738.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值