Windows 邮件槽(MailSlot)
来自《Windows网络编程第二版 中文版》
优点:通过网络,将一条消息广播给一台或多台计算机。
 
缺点:只允许从客户机到服务器,建立一种不可靠的单向数据通信。不提供数据可靠性传播的保障。
 
邮件槽是围绕Windows文件系统接口设计出来的。客户机和服务器应用需要使用标准的Win32文件系统I/O函数,如ReadFile和WriteFile等,以便在邮件槽上收发数据,同时利用Win32文件系统的命名规则。
 
邮件槽的名字
邮件槽标识遵守下述命名规则:
//server/Mailslot/[path]name
第一部分//server对应服务器名,在其上创建邮件槽并在上面运行服务器程序。取值可以是小数点(.),一个星号(*),一个域名或者一个真正的服务器名字。所谓“域”,是一系列工作站和服务器的组合,它们共用一个相同的组名。
第二部分/Mailslot是固定的字符串。
第三部分/[path]name,其中“path”代表路径,可指多级目录。
下面都是合法的名字:
//Oreo/Mailslot/Mymailslot
//Testserver/Mailslot/Cooldirectory/Funtest/Anothermailslot
//./Mailslot/Easymailslot
//*/Mailslot/Myslot
 
消息的长度
邮件槽常用“无连接”形式发送“数据报”(Datagram),不要求对方提供包的收到确认信息。通过无连接可将消息从一个客户机广播给多个服务器。
例外:在Windows NT和Windows 2000中,假如消息长度超过424个字节,必须使用“面向连接”的协议进行传输,而不再使用无连接的“数据报”形式。这样也就不能将一条消息从客户机广播给多个服务器。对于“面向连接”的传输来说,必然是“一对一”通信:一个客户机对一个服务器。
 
应用程序的编译
用VC++ 6.0编制邮件槽应用程序时,必须包含Winbase.h头文件。如果已经包含Windows.h就可省去Winbase.h。应用程序需要与Kernel32.lib链接(是VC++ 6.0默认配置)。
 
错误代码
邮件槽应用开发中,所有Win32 API函数(CreateFile和CreateMailslot除外)在调用失败时都返回0。CreateFile和CreateMailslot这两个API返回INVALID_HANDLE_VALUE(无效句柄值)。调用失败可以使用GetLastError函数接受与此次失败相关的特殊信息。
 
基本客户机/服务器
服务器进程:创建一个邮件槽,能从该邮件槽读数据的唯一一个进程。
客户机:负责打开邮件槽“实例”,是能向其中写入数据的唯一一种进程。
服务器:
1、用CreateMailslot API函数创建一个邮件槽并获得句柄。
2、调用ReadFile API函数,使用已有的邮件槽句柄从任何客户机接收数据。
3、用CloseHandle函数关闭邮件槽句柄。
创建邮件槽:
HANDLE CreateMailslot(
LPCTSTR lpName, //指定邮件槽的名字,如//./Mailslot/[path]name,小数点表示服务器为本的机器(不能为远程计算机创建邮件槽)。
DWORD nMaxMessageSize,//可写入邮件槽的最大消息长度(字节单位),客户机发生消息大于该值服务器不接受该消息;为0,接收任意长度消息。
DWORD lReadTiemout,//等待模式和不等待模式,MAILSLOT_WAIT_FOREVER无限期等待,0立即返回,其它值以毫秒为单位。
LPSECURITY_ATTRIBUTES lpSecurityAttributes//访问控制权限,一般都这位NULL
);
读取邮件槽中数据:
BOOL ReadFile(
HANDLE hFile,//CreateMailslot返回的邮件槽句柄
LPVOID lpBuffer,//和nNumberOfBytesToRead一起决定可从邮件槽读入多少数据;应比CreateMailslot中的nMaxMessageSize。
DWORD nNumberOfBytesToRead,//如果不够大,ReadFile调用失败返回ERROR_INSUFFICIENT_BUFFER错误。
LPWORD lpNumberOfBytesRead,//读取完成后报告读入的实际字节数。
LPOVERLAPPED lpOverlapped//可采用Win32重叠I/O机制;不采用就设为NULL,调用后阻塞直到有数据读入。
);
  1. //Server.cpp
  2. //服务器邮件槽,用于接收客户发送的广播信息
  3. #include <windows.h>
  4. #include <stdio.h>
  5. int main()
  6. {
  7. HANDLE Mailslot;
  8. char buffer[256];
  9. DWORD NumberOfBytesRead;
  10. //Create the mailslot
  11. Mailslot = CreateMailslot("////.//Mailslot//Myslot",0,
  12. MAILSLOT_WAIT_FOREVER,NULL);
  13. if (INVALID_HANDLE_VALUE == Mailslot)
  14. {
  15. printf("Failed to create a mailslot %d/n", GetLastError());
  16. return -1;
  17. }
  18. //Read data from the mailslot forever!
  19. while (0 != ReadFile(Mailslot, buffer, 256, &NumberOfBytesRead, NULL))
  20. {
  21. printf("%.*s/n",NumberOfBytesRead,buffer);
  22. }
  23. CloseHandle(Mailslot);
  24. return 0;
  25. }

客户机:

对一个现有的邮件槽进行引用和写入。
1、使用CreateFile,针对想要向其传送数据的邮件槽,打开指向它的一个引用句柄。
2、调用WriteFile,向邮件槽写入数据。
3、完成数据写入后,用CloseHandle关闭打开的邮件槽句柄。
打开指向邮件槽的一个引用句柄:
HANDLE CreateFile(
LPCTSTR lpFileName,//邮件槽名字
DWORD dwDesiredAccess,//必须为GENERIC_WRITE,因为客户机只能向服务器写入数据。
DWORD dwSharedMode,//必须为FILE_SHARE_READ,运行服务器在邮件槽打开和进行读操作。
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//设为NULL。
DWORD dwCreationDisposition,//设为OPEN_EXISTING,服务器是本地且没有创建邮件槽调用会失败;服务器在远程,该参数无意义。
DWORD dwFlagsAndAttributes,//设为FILE_ATTRIBUTE_NORMAL
HANDLE hTemplateFile//设为NULL
);
写入数据:
BOOL WriteFile(
HANDLE hFile,//CreateFile返回的引用句柄
LPCVOID lpBuffer,//发送的字符串
DWORD nNumberOfBytesToWrite,//发送的字符串长度(一条最大长度为64KB)。
LPDWORD lpNumberOfBytesWritten,//操作完成后实际发送的数据字节数。
LPOVERLAPPED lpOverlapped//邮件槽是“无连接”数据传输,WriteFile函数在不会在I/O调用时等候,所以参数设为NULL
 
 
  1. //Client.cpp
  2. //客户端用于发送广播数据到服务器
  3. #include <windows.h>
  4. #include <stdio.h>
  5. int main(int argc, char *argv[])
  6. {
  7. HANDLE Mailslot;
  8. DWORD BytesWritten;
  9. CHAR ServerName[256];
  10. //从命令行接受要发送数据到的服务器名
  11. if (argc < 2)
  12. {
  13. printf("Usage: client <server name>/n");
  14. return -1;
  15. }
  16. sprintf(ServerName, "////%s//Mailslot//Myslot",argv[1]);
  17. Mailslot = CreateFile(ServerName, GENERIC_WRITE,
  18. FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
  19. if (INVALID_HANDLE_VALUE == Mailslot)
  20. {
  21. printf("WriteFile failed with error %d/n",GetLastError());
  22. return -1;
  23. }
  24. if(0 == WriteFile(Mailslot, "This is a test", 14, &BytesWritten,NULL))
  25. {
  26. printf("WriteFile failed with error %d/n", GetLastError());
  27. return -1;
  28. }
  29. printf("Wrote %d byteds/n", BytesWritten);
  30. CloseHandle(Mailslot);
  31. return 0;
  32. }

服务器其它API

GetMailslotInfo:一旦邮件槽上有消息可以传递,GetMailslotInfo函数负责获取消息的长度信息;利用这个函数,程序可以针对长度不定的进入消息,动态的调节其缓冲区大小。GetMailslotInfo也可以用来对进入数据进行“轮询”。
BOOL GetMailslotInfo(
HANDLE hMailslot,//CreateMailslot返回的邮件槽句柄。
LPDWORD lpMaxMessageSize,//设置可将多大的一条消息写入邮件槽(字节单位)。
LPDWORD lpNextSize,//下一条消息的长度,可能返回MAILSLOT_NO_MESSAGE,表明当前没有等待接收的消息。
LPDWORD lpMessageCount,//函数返回时读入多少个等待的消息。
LPDWORD lpReadTimeout//等待消息写入的时间长度(毫秒)。
);
 
SetMailslotInfo:设置邮件槽的超时值。超过该值读操作不再等待进入的消息。
BOOL SetMailslotInfo(
HANDLE hMailslot,//CreateMailslot返回的邮件槽句柄。
DWORD lReadTimeout//已毫秒为单位指定读操作等待一条消息写入的最长时间,0,没有消息立即返回;MAILSLOT_WAIT_FOREVER永远等待下去。
);
 
不能取消“阻塞”的I/O请求
对于Windows 95和Windows 98,邮件槽服务器用ReadFile接受数据,假如用MAILSLOT_WAIT_FOREVER标志创建一个邮件槽,读请求便会一直等待直到有数据可用为止。如果ReadFile请求尚未完成,服务器应用突然中止运行,应用程序会被永远“挂起”或“阻塞”。只有重启Windows才能取消。
为了解决这个问题,可让服务器在单独一个线程中,打开一个句柄,令其指向自己的邮件槽。并在需要退出时在主线程中发生数据,以终止处于暂停状体的读操作。
  1. //Server2.cpp
  2. //非阻塞服务器邮件槽,用于接收客户发送的广播信息
  3. #include <windows.h>
  4. #include <stdio.h>
  5. #include <conio.h>
  6. BOOL StopProcessing;
  7. DWORD WINAPI ServeMailslot(LPVOID lpParameter);
  8. void SendMessageToMailslot(void);
  9. int main()
  10. {
  11. DWORD ThreadId;
  12. HANDLE MailslotThread;
  13. StopProcessing = FALSE;
  14. MailslotThread = CreateThread(NULL,0,ServeMailslot,
  15. NULL,0,&ThreadId);
  16. printf("Press a key to stop the server/n");
  17. _getch();
  18. //Mark the StopProcessing flag to TRUE so that when ReadFile
  19. //break, our server thread will end
  20. StopProcessing = TRUE;
  21. //Send a message to our mailslot to break the ReadFile call
  22. //in our server
  23. SendMessageToMailslot();
  24. //等待服务线程完成
  25. if (WAIT_FAILED == WaitForSingleObject(MailslotThread, INFINITE))
  26. {
  27. printf("WaitForSingleObject failed with error %d/n",GetLastError());
  28. return -1;
  29. }
  30. return 0;
  31. }
  32. //This function is the mailslot server worker function to
  33. //process all incoming mailslot I/O
  34. DWORD WINAPI ServeMailslot(LPVOID lpParameter)
  35. {
  36. char buffer[2048];
  37. DWORD NumberOfBytesRead;
  38. DWORD Ret;
  39. HANDLE Mailslot;
  40. Mailslot = CreateMailslot("////.//mailslot//myslot",2048,MAILSLOT_WAIT_FOREVER,NULL);
  41. if (INVALID_HANDLE_VALUE == Mailslot)
  42. {
  43. printf("Failed to create a MailSlot %d/n",GetLastError());
  44. return -1;
  45. }
  46. while (0 != (Ret = ReadFile(Mailslot,buffer,2048,&NumberOfBytesRead,NULL)))
  47. {
  48. if (StopProcessing)
  49. break;
  50. printf("Received %d bytes/n", NumberOfBytesRead);
  51. }
  52. CloseHandle(Mailslot);
  53. return 0;
  54. }
  55. //The SendMessageToMailslot function is designed to send a
  56. //simple message to our server so we can break the blocking
  57. //ReadFile API call
  58. void SendMessageToMailslot()
  59. {
  60. HANDLE Mailslot;
  61. DWORD BytesWritten;
  62. Mailslot = CreateFile("////.//mailslot//myslot",GENERIC_WRITE,
  63. FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  64. if (INVALID_HANDLE_VALUE == Mailslot)
  65. {
  66. printf("CreateFile failed with error %d/n", GetLastError());
  67. return;
  68. }
  69. if (0 == WriteFile(Mailslot, "STOP", 4, &BytesWritten, NULL))
  70. {
  71. printf("WriteFile failed with error %d/n", GetLastError());
  72. return;
  73. }
  74. CloseHandle(Mailslot);
  75. }

【转】Windows 邮件槽(MailSlot)的更多相关文章

  1. windows 邮槽mailslot 在服务程序内建立后客户端无权限访问(GetLastError() == 5)的问题

    邮槽创建在服务程序内,可以创建成功, 但外部客户端连接时 m_hMailslot = CreateFile("\\\\.\\mailslot\\zdpMailslot",GENER ...

  2. [Win]进程间通信——邮槽Mailslot

    进程间通信 进程的地址空间是私有的.出于安全性的目的,如果一个进程不具有特殊的权限,是无法访问另外一个进程的内存空间的,也无法知道内存中保存的数据的意义.但是在一些具体的应用情况下需要多个进行相互配合 ...

  3. 【IPC进程间通讯之中的一个】邮槽MailSlot

    IPC进程间通信+邮槽MailSlot                IPC(Inter-Process Communication.进程间通信).        现代计算机採用虚拟内存机制,为进程提 ...

  4. Windows邮件客户端

    换回WIndows 因为要保存邮件 所以需要邮件客户端 Foxmail 腾讯自家 同样需要独立密码 之前的foxmail是全拼 新注册了一个@qq 发现新注册的@qq绑定到的是新的QQ号 算了 去用1 ...

  5. Windows进程间通信(中)

    二.文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容. W ...

  6. 【转】进程间通信方式总结(windows 和linux)

    平时看的书很多,了解的也很多,但不喜欢总结,这不昨天面试的时候被问到了进程间通信的方式,因为没有认真总结过,所以昨天答得不是特别好.现在将linux和windows的进程间通信方式好好总结一下.    ...

  7. 内核对象 windows操作系统

    问题: 什么是内核对象? 答:内核对象实际上时由内核分配的一块内存,而且只能由内核来访问.它时一个数据结构,成员包含有关于该对象的信息.一些成员对于所有对象类型都是一样的,比如对象名称.安全描述.使用 ...

  8. 《Windows核心编程》读书笔记 上

    [C++]<Windows核心编程>读书笔记 这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对 ...

  9. Windows进程间的通信

    一.进程与进程通信   进程间通信(Interprocess Communication, IPC)是指不同的进程之间进行数据共享和数据交换. 二.进程间通信方式 1.  文件映射 注:文件映射是在多 ...

随机推荐

  1. Sublime Text 3 最新注册码激活码 和 Sublime Text 2 注册码

    Sublime是一款很好用的很轻巧的编辑器,堪称一代神级编辑器.此篇文章用于简单学习记录下神器的激活码,不作其他用途.如有侵权,请联系删除,谢谢~~   1.官方下载地址: http://www.su ...

  2. IT兄弟连 JavaWeb教程 过滤器2

    3  多个过滤器的执行顺序 如果一个Web应用中使用一个过滤器不能解决实际中的业务需要,那么可以部署多个过滤器对业务请求进行多次处理,这样做就组成了一个过滤器链.Web服务器在处理过滤器链时,将按过滤 ...

  3. Kali Linux 工具清单

    Kali Linux 工具清单 Information Gathering acccheck ace-voip Amap Automater bing-ip2hosts braa CaseFile C ...

  4. php高并发之opcache

    今天工作的时候接触到客户的一台服务器,业务逻辑比较简单 .估算pv在120w左右吧,用的是阿里云2c4g的服务器.一大早就开始卡顿了,登陆服务器后查看负载到了八九十. 之后就想办法调整一下吧.突然想起 ...

  5. 解决java中按照数字大小来排序文件

    我们想要输出(1.jpg.2.jpg.3.jpg.10.jpg.11.jpg.20.jpg.21.jpg.31.jpg) 突然看到网上一些写法 总结:既然自己按照定义的文件名规则来处理,也可以进行使用 ...

  6. Zepto事件模块源码分析

    Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...

  7. Kendo UI 特效概述

    Kendo UI 特效概述 Kendo UI Fx 提供了一个丰富,可扩展,性能经过优化的工具集合用来完成 HTML 元素的过渡显示.每种特效近可能的使用 CSS Transition ,对于一些老版 ...

  8. 在使用seek()函数时,有时候会报错为 “io.UnsupportedOperation: can't do nonzero cur-relative seeks”,代码如下:

    __author__ = 'ZHHT' #!/usr/bin/env python # -*- coding:utf-8 -*- import os f = open("test1" ...

  9. 编译错误you should not run configure as root (set FORCE_UNSAFE_CONFIGURE=1 in environment to bypass this check)

    解决方法: export FORCE_UNSAFE_CONFIGURE=1

  10. SpringMVC+Thymeleaf 简单使用

    一.简介 1.Thymeleaf 在有网络和无网络的环境下皆可运行,而且完全不需启动WEB应用,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果.浏览器解释 h ...