c++下使用邮槽实现进程间通信

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++下使用邮槽实现进程间通信相关的知识,希望对你有一定的参考价值。

  Windows API提供了邮槽和命名管道两种机制来实现进程间通信,在这里使用C++实现邮槽。

  邮槽是Windows提供的一种进程间单向通信的机制,进程中的一方只能读取(或写入)数据,而另一方只能写入(或读取)数据。这种进程间的通信可以发生在本地或者网络之中。而在使用邮槽之前,服务器端必须先创建邮槽,创建的函数原型如下:

1 HANDLE WINAPI CreateMailslot(
2   _In_     LPCTSTR               lpName,
3   _In_     DWORD                 nMaxMessageSize,
4   _In_     DWORD                 lReadTimeout,
5   _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
6 );

  其中参数lpName表示邮槽的名称。邮槽名称的格式为"\\.\mailslot\YourMailslotName",其中YourMailslotName由用户指定。需要注意的是,在实际编码中反斜杠需要转义;参数nMaxMessageSize表示发送的消息大小的最大值,若设置为0则表示大小为任意值。(实际上邮槽能传输的数据非常小,一般400KB,若数据过大,邮槽可能无法正常工作);参数lReadTimeout表示读取操作的超时时间;参数lpSecurityAttributes表示邮槽的安全属性,置为NULL表示使用默认的安全属性。

  客户端在使用邮槽前必须先打开邮槽,通过函数CreateFile()实现,函数原型如下:

1 HANDLE WINAPI CreateFile(
2   _In_     LPCTSTR               lpFileName,
3   _In_     DWORD                 dwDesiredAccess,
4   _In_     DWORD                 dwShareMode,
5   _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
6   _In_     DWORD                 dwCreationDisposition,
7   _In_     DWORD                 dwFlagsAndAttributes,
8   _In_opt_ HANDLE                hTemplateFile
9 );

  参数的具体设置方法可参考MSDN给出的解释:

  https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx 

  需要注意的是,指定要打开的邮槽时,若程序是在不同主机上运行的,邮槽名称中的点号"."需要改成对方主机的名称。

  在实际的编程过程中,对邮槽的操作与文件一样,都是通过调用函数ReadFile()和WriteFile()实现的,函数原型如下:

 1 BOOL WINAPI ReadFile(
 2   _In_        HANDLE       hFile,
 3   _Out_       LPVOID       lpBuffer,
 4   _In_        DWORD        nNumberOfBytesToRead,
 5   _Out_opt_   LPDWORD      lpNumberOfBytesRead,
 6   _Inout_opt_ LPOVERLAPPED lpOverlapped
 7 );
 8 
 9 BOOL WINAPI WriteFile(
10   _In_        HANDLE       hFile,
11   _In_        LPCVOID      lpBuffer,
12   _In_        DWORD        nNumberOfBytesToWrite,
13   _Out_opt_   LPDWORD      lpNumberOfBytesWritten,
14   _Inout_opt_ LPOVERLAPPED lpOverlapped
15 );

  其中参数lpNumberOfBytesWritten是一个指向DWORD类型的指针,表示实际读取/写入的字节数。

  最终实现的代码如下,实现面向对象的方法实现:

  服务器端:

 1 //header.h
 2 #ifndef HEADER_H
 3 #define HEADER_H
 4 
 5 #include <windows.h>
 6 
 7 #define BUFFER_SIZE 1024
 8 
 9 class MailServer
10 {
11 public:
12     MailServer();
13     MailServer(const MailServer &) = delete;
14     MailServer & operator=(const MailServer &) = delete;
15     ~MailServer();
16     void ReadMail();
17 private:
18     HANDLE h_mail;
19     char buffer[BUFFER_SIZE];
20     DWORD exact_read_num; //指向实际读取的字节数的指针
21 };
22 
23 #endif
 1 //definition.cpp
 2 #include "header.h"
 3 #include <iostream>
 4 
 5 MailServer::MailServer()
 6 {
 7     //邮槽的命名格式为"\\.\mailslot\YourMailslotName",反斜杠需要转义,采用非阻塞式读取方法
 8     h_mail = ::CreateMailslot("\\\\.\\mailslot\\MyMailSlot", 0, 0, nullptr);
 9     if (h_mail == INVALID_HANDLE_VALUE)
10     {
11         std::cerr << "Failed to create a mailslot!\n";
12         ::system("pause");
13         exit(1);
14     }
15     else
16     {
17         std::cout << "Mailslot created successfully..." << std::endl;
18     }
19 }
20 
21 MailServer::~MailServer()
22 {
23     ::CloseHandle(h_mail);
24     std::cout << "Mailslot closed..." << std::endl;
25 }
26 
27 void MailServer::ReadMail()
28 {
29     std::cout << "Reading mail from mailslot..." << std::endl;
30     while (true)
31     {
32         if (::ReadFile(h_mail, buffer, BUFFER_SIZE, &exact_read_num, nullptr))
33         {
34             std::cout << "New mail: " << buffer << std::endl;
35         }
36     }
37 }
 1 //server.cpp
 2 #include "header.h"
 3 
 4 int main()
 5 {
 6     MailServer mail_svr;
 7     mail_svr.ReadMail();
 8     system("pause");
 9     return 0;
10 }

  客户端:

 1 //header.h
 2 #ifndef HEADER_H
 3 #define HEADER_H
 4 
 5 #include "windows.h"
 6 
 7 #define BUFFER_SIZE 1024
 8 
 9 class MailClient
10 {
11 public:
12     MailClient();
13     MailClient(const MailClient &) = delete;
14     MailClient & operator=(const MailClient &) = delete;
15     ~MailClient();
16     void SendMail();
17 private:
18     HANDLE h_mail;
19     char buffer[BUFFER_SIZE];
20     DWORD exact_write_num;
21 };
22 
23 #endif
 1 //definition.cpp
 2 #include "header.h"
 3 #include <iostream>
 4 
 5 MailClient::MailClient()
 6 {
 7     h_mail = ::CreateFile("\\\\.\\mailslot\\MyMailSlot", GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 8     if (h_mail == INVALID_HANDLE_VALUE)
 9     {
10         std::cerr << "Failed to create a mailslot!\n";
11         system("pause");
12         exit(1);
13     }
14     else
15     {
16         std::cout << "Mailslot created successfully..." << std::endl;
17     }
18 }
19 
20 MailClient::~MailClient()
21 {
22     ::CloseHandle(h_mail);
23     std::cout << "Mailslot closed..." << std::endl;
24 }
25 
26 void MailClient::SendMail()
27 {
28     while (true)
29     {
30         std::cout << "Please write a mail: " << std::flush;
31         std::cin.getline(buffer, BUFFER_SIZE);
32         if (strcmp(buffer, "exit") == 0)
33         {
34             std::cout << "User requests to close the mailslot..." << std::endl;
35             break;
36         }
37         else
38         {
39             if (::WriteFile(h_mail, buffer, BUFFER_SIZE, &exact_write_num, nullptr))
40             {
41                 std::cout << "Mail sent successfully..." << std::endl;
42             }
43             else
44             {
45                 std::cerr << "Failed to send the mail...\n";
46                 system("pause");
47                 exit(1);
48             }
49         }
50     }
51 }
1 #include "header.h"
2 
3 int main()
4 {
5     MailClient mail_clt;
6     mail_clt.SendMail();
7     system("pause");
8     return 0;
9 }

 

以上是关于c++下使用邮槽实现进程间通信的主要内容,如果未能解决你的问题,请参考以下文章

两个程序之间的通行(邮槽)

win32邮槽和进程的概念

win32邮槽和进程的概念

进程间通信/广播

进程间通信以模拟欺凌算法[关闭]

c++ 线程间通信方式