一、进程与进程通信
进程间通信(Interprocess Communication, IPC)是指不同的进程之间进行数据共享和数据交换。
二、进程间通信方式
1. 文件映射
注:文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。
使用内存映射文件的一般流程:(reference from: https://blog.csdn.net/qq_20183489/article/details/54646794)
另外,内存映射文件在处理大数据量的文件时表现出了良好的性能(实际上,文件越大,内存映射的优势就越明显)。示例可以参考:https://blog.csdn.net/zzq060143/article/details/54619571
2.2 共享内存
Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF(INVALID_HANDLE_VALUE)来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。
共享内存实现数据共享示例如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //发送数据的进程先启动,用于发送数据,即将数据写入视图 。 2 3 #include "stdafx.h" 4 #include <Windows.h> 5 #include <conio.h> 6 7 #define BUFFER_SIZE 256 8 TCHAR szMapFileName[] = TEXT("MyFileMappingName"); //映射文件名,即共享内存的名称 9 TCHAR szSendData[] = TEXT("Message from the send process."); 10 11 int main() 12 { 13 HANDLE hMapFile = NULL; 14 LPCTSTR pBuf = NULL; 15 16 //1. 创建一个文件映射内核对象 17 hMapFile = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUFFER_SIZE, szMapFileName); //INVALID_HANDLE_VALUE表示创建一个进程间共享的对象 18 if (NULL == hMapFile) 19 { 20 _tprintf(TEXT("Could not create file mapping object (%d). "), GetLastError()); 21 return -1; 22 } 23 24 //2. 将文件数据映射到进程的地址空间 25 pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE); 26 if (NULL == pBuf) 27 { 28 _tprintf(TEXT("Could not map view of file (%d). "), GetLastError()); 29 30 CloseHandle(hMapFile); 31 hMapFile = NULL; 32 33 return -1; 34 } 35 36 //3. 写入到内存中 37 CopyMemory((void*)pBuf, szSendData, _tcslen(szSendData) * sizeof(TCHAR)); 38 _getch(); //这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车 39 40 //4. 从进程的地址空间中撤消文件数据的映像 41 UnmapViewOfFile(pBuf); 42 43 //5. 关闭文件映射对象和文件对象 44 CloseHandle(hMapFile); 45 46 getchar(); 47 return 0; 48 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //接收数据的进程后启动,用于接收数据,即读取视图的数据 2 3 #include "stdafx.h" 4 #include <Windows.h> 5 6 #define BUFFER_SIZE 256 7 TCHAR szMapFileName[] = TEXT("MyFileMappingName"); 8 9 int main() 10 { 11 HANDLE hMapFile = NULL; 12 LPCTSTR pBuf = NULL; 13 14 //1. 打开一个命名的文件映射内核对象 15 hMapFile = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szMapFileName); 16 if (NULL == hMapFile) 17 { 18 _tprintf(TEXT("Could not open file mapping object (%d). "), GetLastError()); 19 return -1; 20 } 21 22 //2. 将文件映射内核对象hFileMapping映射到当前应用程序的进程地址pBuf,通过该指针可以读写共享的内存区域 23 pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE); 24 if (NULL == pBuf) 25 { 26 _tprintf(TEXT("Could not map view of file (%d). "), GetLastError()); 27 28 CloseHandle(hMapFile); 29 hMapFile = NULL; 30 31 return -1; 32 } 33 34 //3. 显示接收到的数据 35 for (int i = 0; i < _tcsclen(pBuf); i++) 36 { 37 _tprintf(TEXT("%c"), *(pBuf + i)); 38 } 39 printf(" "); 40 41 //4. 从进程的地址空间中撤消文件数据的映像 42 UnmapViewOfFile(pBuf); 43 44 //5. 关闭文件映射对象和文件对象 45 CloseHandle(hMapFile); 46 47 getchar(); 48 return 0; 49 }
2.3 管道
管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的-管道的两端点既可读也可写。
(1) 匿名管道(Anonymous Pipe)是在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。
匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。
匿名管道通信过程:
>>父进程读写过程
①创建匿名管道
②创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取/写入句柄赋值给子进程的输入/输出句柄)。
③关闭子进程相关句柄。(进程句柄,主线程句柄)
④对管道读写
>>子进程读写过程
①获得输入输出句柄
②对管道读写
相关函数:
CreatePipe 管道创建
函数原型
BOOL CreatePipe( PHANDLE hReadPipe, // pointer to read handle PHANDLE hWritePipe, // pointer to write handle LPSECURITY_ATTRIBUTES lpPipeAttributes, // pointer to security attributes DWORD nSize // pipe size );
参数说明:
-
-
- hReadPipe 作为返回类型使用,返回管道读取句柄
- hWritePipe 作为返回类型使用,返回管道写入句柄
- lpPipeAttributes 指向SECURITY_ATTRIBUTES结构体的指针,检测返回句柄是否能被子进程继承,如果此参数为NULL,则句柄不能被继承
- nSize 指定管道的缓冲区大小,改大小只是个建议值,系统将用这个值来计算一个适当的缓存区大小,如果此参数是0,系统会使用默认的缓冲区大小
-
返回值
若函数成功返回非零值
若函数失败返回0,详细消息可以调用GetLastError函数获得
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 父进程.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <Windows.h> 6 #include <iostream> 7 using namespace std; 8 9 int main() 10 { 11 const int nBufferLen = 256; 12 13 SECURITY_ATTRIBUTES sa; 14 HANDLE hRead = NULL; 15 HANDLE hWrite = NULL; 16 17 STARTUPINFO sui; 18 PROCESS_INFORMATION pi; 19 20 char szBuffer[nBufferLen] = { 0 }; 21 DWORD dwReadLen = 0; 22 23 BOOL bRet = FALSE; 24 25 //1. 创建匿名管道 26 sa.bInheritHandle = TRUE; 27 sa.lpSecurityDescriptor = NULL; 28 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 29 bRet = ::CreatePipe(&hRead, &hWrite, &sa, 0); 30 if (!bRet) 31 { 32 cout << "创建匿名管道失败!" << endl; 33 system("pause"); 34 return -1; 35 } 36 37 //2. 创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取写入句柄赋予子进程的输入输出句柄) 38 ZeroMemory(&sui, sizeof(STARTUPINFO)); 39 sui.cb = sizeof(STARTUPINFO); 40 sui.dwFlags = STARTF_USESTDHANDLES; 41 sui.hStdInput = hRead; 42 sui.hStdOutput = hWrite; 43 sui.hStdError = GetStdHandle(STD_ERROR_HANDLE); 44 bRet = ::CreateProcess(L"..\x64\Debug\子进程.exe", NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &sui, &pi); 45 if (!bRet) 46 { 47 cout << "创建子进程失败!" << endl; 48 system("pause"); 49 return -1; 50 } 51 52 //3. 关闭子进程相关句柄(进行句柄,进程主线程句柄) 53 CloseHandle(pi.hProcess); 54 CloseHandle(pi.hThread); 55 56 Sleep(2000); 57 58 //4. 读取数据 59 bRet = ::ReadFile(hRead, szBuffer, nBufferLen, &dwReadLen, NULL); 60 if (!bRet) 61 { 62 cout << "读取数据失败!" << endl; 63 system("pause"); 64 return -1; 65 } 66 67 cout << "从子进程接收到到数据: " << szBuffer << endl; 68 69 system("pause"); 70 return 0; 71 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 子进程.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <Windows.h> 6 #include <iostream> 7 using namespace std; 8 9 int main() 10 { 11 HANDLE hRead = NULL; 12 HANDLE hWrite = NULL; 13 14 BOOL bRet = FALSE; 15 16 //1. 获得匿名管道输入输出句柄 17 hRead = GetStdHandle(STD_INPUT_HANDLE); 18 hWrite = GetStdHandle(STD_OUTPUT_HANDLE); 19 20 char szSendBuffer[] = "子进程写入管道成功!"; 21 DWORD dwWriteLen = 0; 22 23 //2. 写入数据 24 bRet = WriteFile(hWrite, szSendBuffer, (DWORD)strlen(szSendBuffer), &dwWriteLen, NULL); 25 if (!bRet) 26 { 27 system("pause"); 28 return -1; 29 } 30 31 Sleep(500); 32 33 system("pause"); 34 return 0; 35 }
(2) 命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。
命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。
命名管道有两种通信方式:
A. 字节模式:在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动
B. 消息模式:在消息模式下,客户机和服务器则通过一系列不连续的数据单位,进行数据收发,每次在管道上发一条消息后,它必须作为一条完整的消息读入
通信流程:
服务器端: 创建命名管道 -> 服务器等待用户连接 -> 读写数据
客户端:连接命名管道 -> 打开命名管道 -> 读写数据
相关函数:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
显示相关函数 [CreateNamePine 创建命名管道][ConnectNamePipe 创建连接命名管道][WaitNamedPipe 进行命名管道连接] CreateNamePipe 创建命名管道 函数原型: HANDLE CreateNamedPipe( LPCTSTR lpName, // pipe name DWORD dwOpenMode, // pipe open mode DWORD dwPipeMode, // pipe-specific modes DWORD nMaxInstances, // maximum number of instances DWORD nOutBufferSize, // output buffer size DWORD nInBufferSize, // input buffer size DWORD nDefaultTimeOut, // time-out interval LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD ); 参数说明: lpName 一个指向空终止的字符串,该字符串的格式必须是:"\.pinepinename"其中该字符串开始是两个连续的反斜杠,其后的原点表示是本地机器,如果想要与远程的服务器建立连接连接,那么在原点这个位置应该指定这个远程服务器的名称.接下来是"pine"这个固定的字符串,也就是说这个字符串的内容不能修改,但其大小写是无所谓的,最后是所创建的命名管道的名称 dwOpenMode 指定管道的访问方式,重叠方式.写直通方式,还有管道句柄的安全访问方式() 用来指定管道的访问方式的标志取值如下(下面这三个值只能够取其中一个),并且管道的每一个实例都必须具有同样的访问方式 PIPE_ACCESS_INBOUND 管道只能用作接收数据(服务器只能读数据,客户端只能写数据),相当于在CreateFile中指定了GENERIC_READ PIPE_ACCESS_OUTBOUND 管道只能用作发送数据(服务器只能写数据,客户端只能读数据),相当于在CreateFile中指定了GENERIC_WRITE PIPE_ACCESS_DUPLEX 管道既可以发送也可以接收数据,相当于在CreateFile中指定了GENERIC_READ | GENERIC_WRITE 用来指定写直通方式和重叠方式的标志,取值可以是一下一个或多个组合 FILE_FLAG_WRITE_THROUGH 管道用于同步发送和接收数据,只有在数据被发送到目标地址时发送函数才会返回,如果不设置这个参数那么在系统内部对于命名管道的处理上可能会因为减少网络附和而在数据积累到一定量时才发送,并且对于发送函数的调用会马上返回 FILE_FLAG_OVERLAPPED 管道可以用于异步输入和输出,异步读写的有关方法和文件异步读写是相同的 用来指定管道安全访问方式的标志,取值可以是一下一个或多个组合 WRITE_DAC 调用者对命名管道的任意范围控制列表(ACL)都可以进行写入访问 WRITE_OWNER 调用者对命名管道的所有者可以进行写入访问 ACCESS_SYSTEM_SECURITY 调用者对命名管道打安全范围控制列表(SACL)可以进行写入访问 dwPipeMode 指定管道类型,,读取和等待方式可以是下面值的组合(0为字节写字节读阻塞方式) 用于指定管道句柄的写入的标志 PIPE_TYPE_BYTE 数据在通过管道发送时作为字节流发送,不能与PIPE_READMODE_MESSAGE共用 PIPE_TYPE_MESSAGE 数据在通过管道发送时作为消息发送,不能与PIPE_READMODE_BYTE共用 用于指定管道句柄的读取方式 PIPE_READMODE_BYTE 在接收数据时接收字节流该方式在PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE类型均可以使用 PIPE_READMODE_MESSAGE 在接收数据时接收消息该方式只用在PIPE_TYPE_MESSAGE类型下才可以使用 用于指定管道句柄的等待方式(同一管道的不同实例可以采取不同的等待方式) PIPE_WAIT 使用等待模式(阻塞方式),在读,写和建立连接时都需要管道的另一方完成相应动作后才会返回 PIPE_NOWAIT 使用非等待模式(非阻塞方式),在读,写和建立连接时不需要管道的另一方完成相应动作后就会立即返回 nMaxInstances 为管道的的最大数量,在第一次建立服务器方管道时这个参数表明该管道可以同时存在的数量。PIPE_UNLIMITED_INSTANCES表明不对数量进行限制 nOutBufferSize 表示输出缓冲区的大小 nInBufferSize 表示输入缓冲区的大小 nDefaultTimeOut 表示在等待连接时最长的等待时间(以毫秒为单位),如果在创建时设置为NMPWAIT_USE_DEFAULT_WAIT表明无限制的等待,而以后服务器方的其他管道实例也需要设置相同的值 lpSecurityAttributes 为安全属性,一般设置为NULL。如果创建或打开失败则返回INVALID_HANDLE_VALUE。可以通过GetLastError得到错误 返回值 若函数成功,返回值是一个命名通道实例的句柄,如果命名通道已经存在则返回一个以存在的命名通道的句柄,并调用GetLastError函数的返回值为 ERROR_ALREADY_EXISTS 若函数失败,返回值为INVALID_HANDLE_VALUE若想获得更多信息调用GetLastError函数获得 ConnectNamePipe 创建命名管道连接 函数原型 BOOL ConnectNamedPipe( HANDLE hNamedPipe, // handle to named pipe LPOVERLAPPED lpOverlapped // overlapped structure ); 参数说明: lpNamedPipe : 指向一个命名管道实例的服务的句柄,该句柄由CreateNamedPipe函数返回 lpOverlapped: 指向一个OVERLAPPED结构的指针,如果hNamedPipe参数所标识的管道是用FILE_FLAG_OVERLAPPED标志打开的,则这个参数不能是NULL,必须是一个有效的指向一个OVERLAPPED的结构指针;否则函数则会错误的执行.如果hNampdPipe参数标志的管道用FILE_FLAG_OVERLAPPED标志打开的,并且这个参数不是NULL,则这个OVERLAPPED结构体必须包含人工重置对象句柄. 返回值 如果函数成功返回非零值如果失败返回0详细消息可以调用GetLastError函数获得 WaitNamedPipe 进行命名管道连接 函数原型 BOOL WaitNamedPipe( LPCTSTR lpNamedPipeName, // pipe name DWORD nTimeOut // time-out interval ); 参数说明: lpNamedPipeName 用来指定管道的名称,这个名称必须包括创建该命名管道的服务器进程所在的机器的名称,该名称的格式必须是"\.pinepinename".如果在同一台机器上编写的命名管道的服务器端程序和客户端程序,则应该指定这个名称时,在开始的两个反斜杆后可以设置一个圆点,表示服务器进程在本地机器上运行;如果是跨网络通信,则在这个圆点位置处应该指定服务器端程序所在的主机名 nTimeOut 指定超时间隔. NMPWAIT_USE_DEFAULT_WAIT 超时间隔就是服务器端创建该命名管道时指定的超时值 NWPWAIT_WAIT_FOREVER 一直等待,直到出现了一个可用的命名管道的实例 也就是说,如果这个参数的值是NMPWAIT_USE_DEFAULT_WAIT,并且在服务器端调用CreateNamedPipe函数创建命名管道时,设置的超时间隔为1000ms,那么一个命名管道的所有实例来说,它们必须使用同样的超时间隔 返回值 如果函数成功返回非零值如果失败返回0详细消息可以调用GetLastError函数获得
Sample Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 服务端.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <Windows.h> 6 #include <iostream> 7 using namespace std; 8 9 int main() 10 { 11 HANDLE hPipe = NULL; 12 HANDLE hEvent = NULL; 13 DWORD dwReadLen = 0; 14 DWORD dwWriteLen = 0; 15 OVERLAPPED ovlap; 16 char senbuf[] = "This is server!"; 17 char rebuf[100]; 18 19 //1. 创建命名管道 20 hPipe = CreateNamedPipe(L"\\.\pipe\Communication", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); 21 if (INVALID_HANDLE_VALUE == hPipe) 22 { 23 cout << "创建命名管道失败!" << endl; 24 hPipe = NULL; 25 system("pause"); 26 return -1; 27 } 28 29 hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 30 if (NULL == hEvent) 31 { 32 cout << "创建事件对象失败!" << endl; 33 CloseHandle(hPipe); 34 hPipe = NULL; 35 system("pause"); 36 return -1; 37 } 38 39 ZeroMemory(&ovlap, sizeof(OVERLAPPED)); 40 ovlap.hEvent = hEvent; 41 42 //2. 创建管道连接 43 if (!ConnectNamedPipe(hPipe, &ovlap)) 44 { 45 if (ERROR_IO_PENDING != GetLastError()) 46 { 47 cout << "等待客户端连接失败!" << endl; 48 CloseHandle(hPipe); 49 CloseHandle(hEvent); 50 hPipe = NULL; 51 system("pause"); 52 return -1; 53 } 54 } 55 56 //3. 等待客户端连接 57 if ( WAIT_FAILED == WaitForSingleObject(hEvent, INFINITE)) 58 { 59 cout << "等待对象失败!" << endl; 60 CloseHandle(hPipe); 61 CloseHandle(hEvent); 62 hPipe = NULL; 63 system("pause"); 64 return -1; 65 } 66 CloseHandle(hEvent); 67 68 //4. 读写管道数据 69 //4.1 读取数据 70 if (!ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL)) 71 { 72 cout << "读取数据失败!" << endl; 73 system("pause"); 74 return -1; 75 } 76 cout << rebuf << endl; 77 78 //4.2 写入数据 79 if (!WriteFile(hPipe, senbuf, (DWORD)strlen(senbuf) + 1, &dwWriteLen, NULL)) 80 { 81 cout << "写入数据失败!" << endl; 82 system("pause"); 83 return -1; 84 } 85 86 system("pause"); 87 return 0; 88 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 客户端.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <Windows.h> 6 #include <iostream> 7 using namespace std; 8 9 int main() 10 { 11 HANDLE hPipe = NULL; 12 HANDLE hEvent = NULL; 13 DWORD dwReadLen = 0; 14 DWORD dwWriteLen = 0; 15 char senbuf[] = "This is client!"; 16 char rebuf[100]; 17 18 //1. 连接命名管道 19 if (!WaitNamedPipe(L"\\.\pipe\Communication", NMPWAIT_WAIT_FOREVER)) 20 { 21 cout << "当前没有可利用的命名管道实例!" << endl; 22 system("pause"); 23 return -1; 24 } 25 26 //2. 打开命名管道 27 hPipe = CreateFile(L"\\.\pipe\Communication", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 28 if (INVALID_HANDLE_VALUE == hPipe) 29 { 30 cout << "打开命名管道失败!" << endl; 31 hPipe = NULL; 32 system("pause"); 33 return -1; 34 } 35 36 //3. 读写管道数据 37 //3.1 写入数据 38 if (!WriteFile(hPipe, senbuf, strlen(senbuf) + 1, &dwWriteLen, NULL)) 39 { 40 cout << "写入数据失败!" << endl; 41 system("pause"); 42 return -1; 43 } 44 45 //3.2 读取数据 46 if (!ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL)) 47 { 48 cout << "读取数据失败!" << endl; 49 system("pause"); 50 return -1; 51 } 52 cout << rebuf << endl; 53 54 system("pause"); 55 return 0; 56 }
2.5 邮件槽
通信流程:
服务器端: 创建邮槽对象 -> 读取数据 -> 关闭邮槽对象
客户端:打开邮槽对象 -> 写入数据 -> 关闭邮槽对象
注意:
(1)邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
(2)邮槽可以实现一对多的单向通信,我们可以利用这个特点编写一个网络会议通知系统,而且实现这一的系统所需要编写的代码非常少.如果读者是项目经理,就可以给你手下每一位员工的机器上安装上这个系统中的邮槽服务器端程序,在你自己的机器上安装油槽的客户端程序,这样,当你想通知员工开会,就可以通过自己安装的邮槽客户端程序.将开会这个消息发送出去,因为机器上都安装了邮槽服务器端的程序,所以他们都能同时收到你发出的会议通知.采用邮槽实现这一的程序非常简单的,如果采用Sockets来实现这一的通信,代码会比较复杂。
(3)邮槽是一种单向通信机制,创建邮槽的服务器进程只能读取数据,打开邮槽的客户机进程只能写入数据。
(4)为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
CreateMailslot函数详解
函数原型:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
HANDLE CreateMailslot( LPCTSTR lpName, // mailslot name DWORD nMaxMessageSize, // maximum message size DWORD lReadTimeout, // read time-out interval LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritance option );
参数说明:
lpName
指向一个空终止字符串的指针,该字符串指定了油槽的名称,该名称的格式必须是:"\.mailslot[path]name ",其中前两个反斜杠之后的字符表示服务器所在机器的名称,圆点表示是主机;接着是硬编码的字符串:"mailslot",这个字符串不能改变,但大小写无所谓;最后是油槽的名称([path]name)由程序员起名
nMaxMessageSize
用来指定可以被写入到油槽的单一消息的最大尺寸,为了可以发送任意大小的消息,卡伊将该参数设置为0
lReadTimeout
指定读写操作的超时间间隔,以ms为单位,读取操作在超时之前可以等待一个消息被写入到这个油槽之中.
如果这个值设置为0,那么若没有消息可用,该函数立即返回;
如果这个值设置为MAILSOT_WAIT_FOREVER,则函数一直等待,直到有消息可用
lpSecurityAttributes
指向一个SECURITY_ATTRIBUTES结构的指针,可以简单地给这个参数传递NULL值,让系统为所创建的油槽赋予默认的安全描述符
Sample Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 服务端.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include<windows.h> 6 #include<iostream> 7 using namespace std; 8 9 int main() 10 { 11 HANDLE hMailslot = INVALID_HANDLE_VALUE; 12 char buf[100] = { '