转 邮件槽(Mailslots)

http://blog.sina.com.cn/s/blog_4b44e1c00100dh2u.html
不同计算机之间的进程通信--邮件槽(Mailslots)

标签:

计算机之间

进程通信

邮槽

邮件槽

mailslots

it

分类: 编程技术

--------理论---------:

定义:
    Microsoft Windows NT、Windows 2000、Windows 95和Windows 98(含第二版)-但不包括Windows CE-提供了一种简单的单向"进程间通信"(interprocess communication,I P C)机制。这个机制的名字非常古怪,叫作"邮件槽"(Mailslots)。用最简单的话来说,通过邮件槽,客户机进程可将消息传送或广播给一个或多个服务器进程。在同一台计算机的不同进程之间,或在跨越整个网络的不同计算机的进程之间,协助进行消息的传输。
    邮件槽(Mailslots)提供进程间单向通信能力,任何进程都能建立邮件槽成为邮件槽服务器。其它进程,称为邮件槽客户,可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消息一直放在邮件槽中,直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户,因此可建立多个邮件槽实现进程间的双向通信。
    通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过424字节,非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。
    邮件槽与命名管道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的,一旦网络发生错误则无法保证消息正确地接收,而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一种选择。

 

传输安全性:
由于邮槽是围绕一个广播通信体系设计出来的,所以当然不能指望能通过它实现数据的"可靠"传输;

 

优缺点:
邮槽最大的一个缺点便是只允许从客户机到服务器,建立一种不可靠的单向数据通信。
而另一方面,邮槽最大的一个优点在于,它们使客户机应用能够非常容易地将广播消息发送给一个或多个服务器应用。

 

协议无关性
由于邮槽要依赖Windows文件系统服务在网上来创建和传输数据,所以接口是"与协议无关"的。
编制自己的应用程序时,便不必关心基层网络传送协议的细节,不必知道它在一个网络中如何在进程之间建立通信。

 

与windows重定向器的密切关系
邮槽通过一个网络与计算机进行远程通信时,windows文件系统服务需要依赖windows重定向器,
使用"服务器消息块"(S M B)协议,将数据从客户机传给服务器。
消息通常是通过"无连接"传输方式来发送的,但亦可强迫Windows重定向器在Windows NT和Windows 2000上使用"面向连接"的传输方式。至于具体采用哪种方式,要由消息的长度决定。

 

无连接与面向连接
邮槽通常用"数据报"(Datagram),以一种"无连接"的方式在网络上传递消息。
但在Windows NT和Windows 2000中,若消息长度超过426个字节,便必须在一个SMB会话之上,
通过一种"面向连接"的协议进行传输,而不再采用无连接的"数据报"形式。
这样一来,在消息较大的情况下,便可保证它们的稳定、高效传输。
然而,此时再也不能将一条消息从客户机广播给多个服务器。

对于"面向连接"的传输来说,它必然是一种"一对一"通信:一个客户机对一个服务器!
在不同的进程之间,使用"面向连接"的传输方式,往往可保证数据传输的可靠性。

 

小结:
要想保证各种Windows平台之间能够完全正常地通信,强烈建议将消息长度限制在424字节,或者更短。
如果进行面向连接的传输,可考虑使用命名管道,而不是简单的邮槽


--------实践---------


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

邮槽服务器的详情
若想实现一个邮槽,要求开发一个服务器应用,来负责邮槽的创建。
下述步骤解释了如何编写一个基本的服务器应用:
1) 用CreateMailslot API函数创建一个邮槽句柄。
2) 调用ReadFile API函数,并使用现成的邮槽句柄,从任何客户机接收数据。
3) 用CloseHandle这个API函数,关闭邮槽句柄。

可以看出,要开发一个邮槽服务器程序,只需使用极少的API调用。
服务器进程是用CreateMailslot这个API调用来创建邮槽的。定义如下:

HANDLE CreateMailslos(
  LPCTSTR lpName,
  DWORD nMaxMessageSize,
  DWORD lReadTimeout,
  LPSECURITY_ATTURIBUTES lpSecurityAttributes
  )
参数说明:
lpName
参数lpName指定邮槽的名字。名字的格式如下:
// ./Mailslot/[path]name
要注意的是,服务器的名字用一个小数点来表示,亦即服务器就是本地机器。
这样做是很有必要的,因为我们不能在远程计算机上创建邮槽。
在lpName参数中,名字必须以一种独一无二的形式表达。
可将它设为一个独立的名字,也可以在它前面加上一个完整的目录路径。
nMaxMessageSize
nMaxMessageSize参数定义的是可写入邮槽的一条消息的最大长度(以字节为单位)。
假如客户机写入的字节数多于nMaxMessageSize的设置,服务器便不会接收这条消息。
若将它的值设为0,服务器便会接收任意长度的消息。
lReadTimeout
在一个邮槽上,读操作可以等待或不等待这两种模式进行,具体由lReadTimeout参数决定。
它以毫秒为单位,指定了读操作需要等候进入消息的时间。
若将它的值设为MAILSLOT_WAIT_FOREVER,那么在进入的数据可以读取之前,读操作便会无限期地等待下去。
若设为0 ,读操作就会立即返回。
LpSecurityAttributes
LpSecurityAttributes参数决定了为邮槽施加的访问控制权限

用一个有效的句柄创建了邮槽之后,便可开始数据的实际读取。服务器是唯一能从邮槽
读入数据的进程。服务器应使用ReadFile这个Win32函数,来进行数据的读取。
对ReadFile的定义如下:
BOOL ReadFile(
 HANDLE hFile,
 LPVOID lpBuffer,
 DWORD nNumberOfBytesToRead,
 LPDOWRD lpNumberOfBytesRead,
 LPOVERLAPPED lpOverlapped
 )
参数说明如下:
CreateMailslot会返回一个句柄hFile。
lpBuffer和nNumberOfBytesToRead参数决定了可从邮槽读入多少数据。
特别值得注意的是,这个缓冲区的大小应该比来自CreateMailslot API调用的nMaxMessageSize参数的设置大。
此外,缓冲区应该大于邮槽上的进入消息;
如果不够大,ReadFile 调用便会失败,并返回一个ERROR_INSUFFICIENT_BUFFER 错误。
lpNumberOfBytesRead参数用于在ReadFile操作完成后,报告读入的实际字节数量。
利用lpOverlapped参数,我们可以通过异步方式,进行数据的读取。

服务器邮槽示例
#include <stdio.h>

#include <Windows.h>

int main(void)
{
 HANDLE Mailslot;
 char Buffer[256];
 DWORD NumberOfBytesRead;

 if ((Mailslot = CreateMailslot("////.//Mailslot//Myslot",0,MAILSLOT_WAIT_FOREVER,NULL)) == INVALID_HANDLE_VALUE)
 {
  printf("Failed to Create a mailslot %d/n",GetLastError());
  return -1;
 }
 
 while (ReadFile(Mailslot,Buffer,256,&NumberOfBytesRead,NULL) != 0)
 {
  printf("%.*s/n",NumberOfBytesRead,Buffer);
 }
 return 0;
}

邮槽客户机的详情


要想实现一个客户机,需要开发一个应用程序,对一个现有的邮槽进行引用和写入。
下述步骤解释了如何编写一个基本的客户机应用:
1) 使用CreateFile这个API函数,针对想向其传送数据的邮槽,打开指向它的一个引用句柄。
2) 调用WriteFile这个API函数,向邮槽写入数据。
3) 完成了数据的写入后,用CloseHandle这个API函数,关闭打开的邮槽句柄。

如前所述,采用一种"无连接"的形式,邮槽客户机同邮槽服务器通信。
客户机打开指向邮槽的一个引用句柄时,实际并不建立同邮槽服务器的一个连接。


要想对一个邮槽进行引用,需要使用CreateFile这个API调用。对它的定义如下:
HANDLE CreateFile(
  LPCSTR lpFileName,
  DWORD dwDesierdAccess,
  DWORD dwShareMode,
  LPSECURITY_ATTURIBUTES lpSecurityAttributes,
  DWORD dwCreationDisposition,
  DWORD dwFlagAndAttributes,
  HANDLE hTempLateFile
  );

参数说明:
lpFileName参数用于描述一个或多个邮槽,可以采用如下的邮槽命名格式,向其写入数据。
//./mailslot/name 标定同一台机器上的一个本地邮槽
//servername/mailslot/name 标定名为servername的一个远程邮槽服务器
//domainname/mailslot/name 标定在指定的domain(域)内,使用特定name(名字)的所有邮槽
//*/mailslot/name 标定系统主域内,标定特定name(名字)的所有邮槽

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

 

 

成功创建一个句柄后,便可开始向邮槽写入数据。请记住,作为客户机,只能将数据写入邮槽。
这可以用Win32函数WriteFile来做到,定义如下:
BOOL WriteFile(
 HANDLE hFile,
 LPCVOID lpBuffer,
  DWORD nNumberOfBytesToWrite,
 LPDWORD lpNumberOfBytesWrite,
 LPOVERLAPPED lpOverlapped
);
参数说明:
其中,hFile参数是由CreateFile返回的一个引用句柄。
lpBuffer和nNumberOfBytesToWrite参数决定了有多少字节从客户机发向服务器。
一条消息的最大长度为6 4 K B。 如果当初是用一个域或星号(*)格式来创建邮槽句柄,
那么在Windows NT和Windows 2000中,消息的长度限制在4 2 4字节之内;
而在Windows 95和Windows 98中,限制在6 4 K B之内。
如客户机试图发送的消息超出了这一长度限制, WriteFile函数会便失败,
而且GetLastError函数会返回ERROR_BAD_NETPATH错误。
之所以会出现这一情况,是由于需要以广播数据报的形式,把消息发送给网络中的所有服务器。
lpNumberOfBytesWritten参数返回的是当WriteFile操作完成后,传给服务器的实际字节数量。
通过lpOverlapped参数,我们可采用异步形式,将数据写入一个邮槽。
由于邮槽最大的特点便是"无连接"的数据传输,所以WriteFile函数不会在I/O调用的时候暂停等候。
在客户机上,这个参数应设为NULL。


客户机邮槽实例
#include "stdafx.h"
#include <stdio.h>

#include <Windows.h>

int _tmain()
{
 HANDLE Mailslot;
 DWORD BytesWritten;
 char ServerName[256];
 
 if (Mailslot == CreateFile("////%s//Mailslot//Myslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL) == INVALID_HANDLE_VALUE)
 {
  printf("CreateFile failed with Error:%d/n",GetLastError());
  return -1;
 }

 if (WriteFile(Mailslot,"This is b Test!",14,&BytesWritten,NULL) == 0)
 {
  printf("WriteFile with error:%d/n",GetLastError());
  return -1;
 }


注:

《windows网络编程》读书笔记 

2005.01.21

你可能感兴趣的:(windows,api,网络,服务器,null,buffer)