zoukankan      html  css  js  c++  java
  • 【转】Windows 邮件槽(MailSlot)

    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.     
    31.     return 0;
    32. }
    33. //This function is the mailslot server worker function to
    34. //process all incoming mailslot I/O
    35. DWORD WINAPI ServeMailslot(LPVOID lpParameter)
    36. {
    37.     char buffer[2048];
    38.     DWORD NumberOfBytesRead;
    39.     DWORD Ret;
    40.     HANDLE Mailslot;
    41.     Mailslot = CreateMailslot("////.//mailslot//myslot",2048,MAILSLOT_WAIT_FOREVER,NULL);
    42.     if (INVALID_HANDLE_VALUE == Mailslot)
    43.     {
    44.         printf("Failed to create a MailSlot %d/n",GetLastError());
    45.         return -1;
    46.     }
    47.     while (0 != (Ret = ReadFile(Mailslot,buffer,2048,&NumberOfBytesRead,NULL)))
    48.     {
    49.         if (StopProcessing)
    50.             break;
    51.         printf("Received %d bytes/n", NumberOfBytesRead);
    52.     }
    53.     CloseHandle(Mailslot);
    54.     return 0;
    55. }
    56. //The SendMessageToMailslot function is designed to send a
    57. //simple message to our server so we can break the blocking
    58. //ReadFile API call
    59. void SendMessageToMailslot()
    60. {
    61.     HANDLE Mailslot;
    62.     DWORD BytesWritten;
    63.     Mailslot = CreateFile("////.//mailslot//myslot",GENERIC_WRITE, 
    64.         FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    65.     if (INVALID_HANDLE_VALUE == Mailslot)
    66.     {
    67.         printf("CreateFile failed with error %d/n", GetLastError());
    68.         return;
    69.     }
    70.     if (0 == WriteFile(Mailslot, "STOP", 4, &BytesWritten, NULL))
    71.     {
    72.         printf("WriteFile failed with error %d/n", GetLastError());
    73.         return;
    74.     }
    75.     CloseHandle(Mailslot);
    76. }
  • 相关阅读:
    SpringBoot2.0之一 新建项目helloWorld
    spring boot 的maven设置阿里云仓库
    新建SpringBoot项目运行页面报错Whitelabel Error Page This application has no explicit mapping for /error, so yo
    SpringBoot2.0 最简单的 idea 快速创建项目
    postgresql 按日期范围查询
    postgreSQL 应用case when的例子
    PostgreSQL 数据库NULL值的默认排序行为与查询、索引定义规范
    ASP.NET中在不同的子域中共享Session
    YKCW6-BPFPF-BT8C9-7DCTH-QXGWCYQ7PR-QTHDM-HCBCV-9GKGG-TB2TM
    Asp.net中基于Forms验证的角色验证授权
  • 原文地址:https://www.cnblogs.com/zhanjxcom/p/4525377.html
Copyright © 2011-2022 走看看