Windows进程间通信-命名管道
一、介绍
Windows平台上有很多种进程间通信(IPC:Inter-Process Communication)的方法。我将把他们列举出来,并谈谈它们的优缺点。IPC定义:
- 进程间通信(IPC)主要是处理的技术和机制是方便进程间通信。
- 操作系统支持的一种进程和进程通信。这些进程可以是运行在同一台机器上,也可以是通过网络互连的不同计算机上的进程。
二、背景
通常一个一定大小软件或者工程都需要两个或者多个进程相互通信。通信的方式有多种。比如:如果进程使用消息队列,那么你可以使用消息WM_COPYDATA在进程间传递信息。Windows中的剪切板也可以利用。
COM/DCOM/RPC 这些技术也能用于进程间通信,这些技术超出了本文。
还有一些技术可能会提到:
- 共享内存
- 命名管道(Named Pipe)
- Socket通信(WinSock)
- 油槽(Mailslot)
理解Windows同步时学习IPC的基础。几乎每个IPC机制的使用都伴随着进程间同步。一下是一些进程间用来同步的同步对象:
- Mutex
- Semaphore
- Events
还有一个同步对象--临界区。然而,临界区是用来控制一个进程、多个线程间的同步的。它不能用于进程间同步。Windows同步的详细讨论就不深入了。
三、代码实现
3、1共享内存
下面的一些API是共享内存(内存映射对象)工作时使用的:
- CreateFileMapping()
- MapViewOfFile()
- UnMapViewOfFile()
- CloseHandle()
共享内存特点:
- 代码具有平台依赖性
- 只能在一台计算机中的进程中使用
- 一旦进程映射对象映射到进程区域,用起来非常容易
- 也许是所有IPC中最快的方式了
共享内存实现进程间通信的源码在另一篇文章中:3、2命名管道(Named Pipe)
命名一个对象有助于进程间共享对象的处理。名字区分大小写。已命名对象是内核对象,内核对象是特定进程相关的,这个进程使用它们之前需要先创建。
管道遵循先进先出原则,有两种类型的管道:匿名管道,命名管道
创建匿名管道使用函数CreatePipe(),匿名管道存在与本地,不能用于在网络上传输。匿名管道没有名字,单向的,通常用来从父进程向子进程传递数据。
命名管道可以单向或者多项传输数据。命名管道可以在一台计算机或者通过网络连接的多台计算机中相互通信。
下面是一些Win32命名管道使用的API:
- CreateNamedPipe()
- ConnectNamedPipe()
- WaitNamedPipe()
- DisconnectNamedPipe()
- ReadFile()
- WriteFile()
- CloseHandle()
命名管道进程通信服务端的代码:#include "stdafx.h" #include "windows.h" //Name given to the pipe #define g_szPipeName "\\\\.\\Pipe\\MyNamedPipe" //Pipe name format - \\.\pipe\pipename #define BUFFER_SIZE 1024 //1k #define ACK_MESG_RECV "Message received successfully" int main(int argc, char* argv[]) { HANDLE hPipe; hPipe = CreateNamedPipe( g_szPipeName, // pipe name PIPE_ACCESS_DUPLEX, // read/write access PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // max. instances BUFFER_SIZE, // output buffer size BUFFER_SIZE, // input buffer size NMPWAIT_USE_DEFAULT_WAIT, // client time-out NULL); // default security attribute if (INVALID_HANDLE_VALUE == hPipe) { printf("\nError occurred while " "creating the pipe: %d", GetLastError()); return 1; //Error } else { printf("\nCreateNamedPipe() was successful."); } printf("\nWaiting for client connection..."); //Wait for the client to connect BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL); if (FALSE == bClientConnected) { printf("\nError occurred while connecting" " to the client: %d", GetLastError()); CloseHandle(hPipe); return 1; //Error } else { printf("\nConnectNamedPipe() was successful."); } char szBuffer[BUFFER_SIZE]; DWORD cbBytes; //We are connected to the client. //To communicate with the client //we will use ReadFile()/WriteFile() //on the pipe handle - hPipe //Read client message BOOL bResult = ReadFile( hPipe, // handle to pipe szBuffer, // buffer to receive data sizeof(szBuffer), // size of buffer &cbBytes, // number of bytes read NULL); // not overlapped I/O if ( (!bResult) || (0 == cbBytes)) { printf("\nError occurred while reading " "from the client: %d", GetLastError()); CloseHandle(hPipe); return 1; //Error } else { printf("\nReadFile() was successful."); } printf("\nClient sent the following message: %s", szBuffer); strcpy(szBuffer, ACK_MESG_RECV); //Reply to client bResult = WriteFile( hPipe, // handle to pipe szBuffer, // buffer to write from strlen(szBuffer)+1, // number of bytes to write, include the NULL &cbBytes, // number of bytes written NULL); // not overlapped I/O if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes)) { printf("\nError occurred while writing" " to the client: %d", GetLastError()); CloseHandle(hPipe); return 1; //Error } else { printf("\nWriteFile() was successful."); } CloseHandle(hPipe); return 0; //Success }
命名管道客户端:#include "stdafx.h" #include "windows.h" //Name given to the pipe #define g_szPipeName "\\\\.\\Pipe\\MyNamedPipe" //Pipe name format - \\servername\pipe\pipename //This pipe is for server on the same computer, //however, pipes can be used to //connect to a remote server #define BUFFER_SIZE 1024 //1k #define ACK_MESG_RECV "Message received successfully" int main(int argc, char* argv[]) { HANDLE hPipe; //Connect to the server pipe using CreateFile() hPipe = CreateFile( g_szPipeName, // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe 0, // default attributes NULL); // no template file if (INVALID_HANDLE_VALUE == hPipe) { printf("\nError occurred while connecting" " to the server: %d", GetLastError()); //One might want to check whether the server pipe is busy //This sample will error out if the server pipe is busy //Read on ERROR_PIPE_BUSY and WaitNamedPipe() for that return 1; //Error } else { printf("\nCreateFile() was successful."); } //We are done connecting to the server pipe, //we can start communicating with //the server using ReadFile()/WriteFile() //on handle - hPipe char szBuffer[BUFFER_SIZE]; printf("\nEnter a message to be sent to the server: "); gets(szBuffer); DWORD cbBytes; //Send the message to server BOOL bResult = WriteFile( hPipe, // handle to pipe szBuffer, // buffer to write from strlen(szBuffer)+1, // number of bytes to write, include the NULL &cbBytes, // number of bytes written NULL); // not overlapped I/O if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes)) { printf("\nError occurred while writing" " to the server: %d", GetLastError()); CloseHandle(hPipe); return 1; //Error } else { printf("\nWriteFile() was successful."); } //Read server response bResult = ReadFile( hPipe, // handle to pipe szBuffer, // buffer to receive data sizeof(szBuffer), // size of buffer &cbBytes, // number of bytes read NULL); // not overlapped I/O if ( (!bResult) || (0 == cbBytes)) { printf("\nError occurred while reading" " from the server: %d", GetLastError()); CloseHandle(hPipe); return 1; //Error } else { printf("\nReadFile() was successful."); } printf("\nServer sent the following message: %s", szBuffer); CloseHandle(hPipe); return 0; //Success }
特点:
- 源码具有平台相关性
- 命名管道用起来简单
- 能通过网络通信
3、3Socket通信(WinSock)
WinSock提供了很高的网络能力。支持TCP/IP协议,同时还有其他协议---AppleTalk, DECNet, IPX/SPX等
WinSock使用的函数:
socket()
bind()
listen()
accept()
connect()
send()
recv()
特点:
- 使用广泛,本机或者网络都能运行。而且能跨平台和协议
- 使用WinSock需要扎实的网络基础知识。
3、4油槽(Mailslot)
油槽用于进程间的单向通信。油槽服务器是只读的,它从客户端读取数据并发送。客户端是只写的,然后发送信息给服务端。油槽信息只能传输大约400字节。
油槽能在一定的域范围内广播消息。如果一个域中的进程创建了相同名字的油槽,那么消息就会发送给所有进程。
油槽的函数:
油槽服务端代码:
CreateMailSlot()
GetMailslotInfo()
SetMailslotInfo()
ReadFile()
WriteFile()
CloseHandle()
#include "stdafx.h" #include "windows.h" //Name given to the Mailslot #define g_szMailslot "\\\\.\\mailslot\\MyMailSlot" //Mailslot name format - \\.\mailslot\mailslotname #define BUFFER_SIZE 1024 //1k int main(int argc, char* argv[]) { HANDLE hMailslot; hMailslot = CreateMailslot( g_szMailslot, // mailslot name BUFFER_SIZE, // input buffer size MAILSLOT_WAIT_FOREVER, // no timeout NULL); // default security attribute if (INVALID_HANDLE_VALUE == hMailslot) { printf("\nError occurred while" " creating the mailslot: %d", GetLastError()); return 1; //Error } else { printf("\nCreateMailslot() was successful."); } //Mailslot is one-way communication //Server will only read //Using ReadFile() char szBuffer[BUFFER_SIZE]; DWORD cbBytes; BOOL bResult; printf("\nWaiting for client connection..."); while(1) //Infinite, till user terminates the console app { //Read client message bResult = ReadFile( hMailslot, // handle to mailslot szBuffer, // buffer to receive data sizeof(szBuffer), // size of buffer &cbBytes, // number of bytes read NULL); // not overlapped I/O if ( (!bResult) || (0 == cbBytes)) { printf("\nError occurred while reading" " from the client: %d", GetLastError()); CloseHandle(hMailslot); return 1; //Error } else { printf("\nReadFile() was successful."); } printf("\nClient sent the following message: %s", szBuffer); } CloseHandle(hMailslot); return 0; //Success }
油槽客户端代码:#include "stdafx.h" #include "windows.h" //Name given to the Mailslot #define g_szMailslot "\\\\.\\mailslot\\MyMailSlot" //Mailslot name format - \\.\mailslot\mailslotname //This mailslot is for server on the same computer, //however, mailslots can be used to //connect to a remote server #define BUFFER_SIZE 1024 //1k int main(int argc, char* argv[]) { HANDLE hMailslot; //Connect to the server mailslot using CreateFile() hMailslot = CreateFile( g_szMailslot, // mailslot name GENERIC_WRITE, // mailslot write only FILE_SHARE_READ, // required for mailslots NULL, // default security attributes OPEN_EXISTING, // opens existing mailslot FILE_ATTRIBUTE_NORMAL, // normal attributes NULL); // no template file if (INVALID_HANDLE_VALUE == hMailslot) { printf("\nError occurred while connecting" " to the server: %d", GetLastError()); return 1; //Error } else { printf("\nCreateFile() was successful."); } //We are done connecting to the mailslot, //Mailslot communication is one-way, //client will just write to mailslot //Using WriteFile() char szBuffer[BUFFER_SIZE]; printf("\nEnter a message to be sent to the server: "); gets(szBuffer); DWORD cbBytes; //Send the message to server BOOL bResult = WriteFile( hMailslot, // handle to mailslot szBuffer, // buffer to write from strlen(szBuffer)+1, // number of bytes to write, include the NULL &cbBytes, // number of bytes written NULL); // not overlapped I/O if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes)) { printf("\nError occurred while writing" " to the server: %d", GetLastError()); CloseHandle(hMailslot); return 1; //Error } else { printf("\nWriteFile() was successful."); } CloseHandle(hMailslot); return 0; //Success }
特点:
- 代码具有平台相关性
- 油槽只能提供单向的数据传送
- 消息的大小限制在400字节左右
- 油槽支持广播
- 能通过网络通信
- 使用简单
四、总结
本文简要介绍Windows IPC机制。试图可用选项列表,并提供了一些代码来帮助读者了解如何使用Windows IPC机制。这篇文章不是很详尽的或详细的。读者需要引用其他详细的关于这一主题的资料了解Windows IPC在一个更好的方式。