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 }