WIndows进程通信(IPC)之管道通信

WIndows进程通信(IPC)之管道通信

Windows下用管道通信(pipe)实现进程间数据共享管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。管道分为匿名管道和命名管道。

匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。

命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。创建匿名管道:

1. 定义安全属性结构体:

SECURITY_ATTRIBUTES sa;

sa.bInheritHandle = TRUE;//表示可被子进程所继承

sa.lpSecurityDescriptor = NULL; //安全描述符号一般都设置成NULL,即默认描述符

sa.nLength = sizeof(SECURITY_ATTRIBUTES); //管道长度

其中SECURITY_ATTRIBUTES结构体的定义为:

typedef struct _SECURITY_ATTRIBUTES {

DWORD nLength;

LPVOID lpSecurityDescriptor;

BOOL bInheritHandle;

} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

2. 创建管道:

BOOL WINAPI CreatePipe(

PHANDLE hReadPipe,//读取端句柄

PHANDLE hWritePipe,//输入端句柄

LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全属性

DWORD nSize// 管道的缓冲区容量,NULL表示默认大小

);

3. 读取管道内数据:

BOOL ReadFile(

HANDLE hFile,//句柄,可以是标准输入输出流或文件或管道

LPVOID lpBuffer, //读取的数据写入缓冲区

DWORD nNumberOfBytesToRead,//指定读取的字节数

LPDWORD lpNumberOfBytesRead,//实际读取的字节数

LPOVERLAPPED lpOverlapped//用于异步操作,一般置为NULL

);

4. 向管道内写入数据:

BOOL WriteFile(

HANDLE hFile,//句柄,同上

LPCVOID lpBuffer,//指定待写入的数据

DWORD nNumberOfBytesToWrite,//写入的数据量

LPDWORDlp NumberOfBytesWritten,//实际要写的数据量

LPOVERLAPPED lpOverlapped//一般置为NULL

);

5. 为实现父子进程间的通信,需要对子进程的管道进行重定向:我们知道创建子进程函数 CreateProcess中有一个参数STARUIINFO,默认情况下子进程的输入输出管道是标准输入输出流,可以通过下面的方法实现管道重定向:

STARTUPINFO si;

si.hStdInput = hPipeInputRead; //输入由标准输入 -> 从管道中读取

si.hStdOutput = hPipeOutputWrite; //输出由标准输出 -> 输出到管道

创建命名管道:命名管道有点类似我们常听见的服务器端和客户端,管道正好起着传输正如他的名字,命名管道有自己的名字,首先要指定管道名,管道名遵循的格式为:

\\.\pipe\pipename。最多可达256个字符的长度,而且不区分大小写例如:"\\\\.\\pipe\\Name_pipe_demon_get"服务器端创建命名管道

HANDLE WINAPI CreateNamedPipe(

LPCTSTRlpName,//管道名

DWORD dwOpenMode,//管道打开方式

//PIPE_ACCESS_DUPLEX 该管道是双向的,服务器和客户端进程都可以从管道读取或者向管道写入数据。

//PIPE_ACCESS_INBOUND 该管道中数据是从客户端流向服务端,即客户端只能写,服务端只能读。

//PIPE_ACCESS_OUTBOUND 该管道中数据是从服务端流向客户端,即客户端只能读,服务端只能写。

DWORD dwPipeMode,//管道的模式

//PIPE_TYPE_BYTE 数据作为一个连续的字节数据流写入管道。

//PIPE_TYPE_MESSAGE 数据用数据块(名为“消息”或“报文”)的形式写入管道。

//PIPE_READMODE_BYTE 数据以单独字节的形式从管道中读出。

//PIPE_READMODE_MESSAGE 数据以名为“消息”的数据块形式从管道中读出(要求指定PIPE_TYPE_MESSAGE)。

//PIPE_WAIT 同步操作在等待的时候挂起线程。

//PIPE_NOWAIT 同步操作立即返回。

DWORD nMaxInstances,//表示该管道所能够创建的最大实例数量。必须是1到常数PIPE_UNLIMITED_INSTANCES(255)间的一个值。

DWORD nOutBufferSize,//表示管道的输出缓冲区容量,为0表示使用默认大小。

DWORD nInBufferSize,//表示管道的输入缓冲区容量,为0表示使用默认大小。

DWORD nDefaultTimeOut,//表示管道的默认等待超时。

LPSECURITY_ATTRIBUTES lpSecurityAttributes//表示管道的安全属性。

);

创建完成后等待连接

BOOL WINAPI ConnectNamedPipe(

HANDLE hNamedPipe,//命名管道句柄

LPOVERLAPPED lpOverlapped//一般为NULL

);

服务器端就绪后,客户端开始连接

BOOL WINAPI WaitNamedPipe(

LPCTSTR lpNamedPipeName,//命名管道名称

DWORD nTimeOut//等待时长

);

连接成功后,打开管道进行数据通信,使用CreateFile,ReadFile和WriteFile,前面匿名管道已经给出了具体使用方法。下面来看一个具体的例子A程序作为服务器,不断从B程序接收数据,并发送到C程序中:

#include

#include

#include

#include

#include

#include

const char *pStrPipeNameGet = "\\\\.\\pipe\\Name_pipe_demon_get";

const char *pStrPipeNameSend = "\\\\.\\pipe\\Name_pipe_demon_send";

const int BUFFER_MAX_LEN = 1024;

char buf[BUFFER_MAX_LEN];

DWORD dwLen;

HANDLE get, mSend, mutex;

LPCRITICAL_SECTION cs;

WCHAR* toWChar(const char *c){

WCHAR wszClassName[256];

memset(wszClassName, 0, sizeof(wszClassName));

MultiByteToWideChar(CP_ACP, 0, c, strlen(c) + 1, wszClassName,

sizeof(wszClassName) / sizeof(wszClassName[0]));

return wszClassName;

}

void beginGetThread(PVOID p){

printf("服务器Get\n");

printf("等待连接......\n");

HANDLE hPipe = CreateNamedPipe(toWChar(pStrPipeNameGet), PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);

if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待连接。

{

printf("连接成功,开始接收数据\n");

while (true)

{

WaitForSingleObject(mutex, INFINITE);

EnterCriticalSection(cs);

//接收客户端发送的数据

ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);

printf("接收到来自A的数据长度为%d字节\n", dwLen);

printf("具体数据内容如下:");

int bufSize;

for (bufSize = 0; bufSize < (int)dwLen; bufSize++){

putchar(buf[bufSize]);

}

LeaveCriticalSection(cs);

Sleep(500);

ReleaseSemaphore(mutex, 1, NULL);

putchar('\n');

}

}

else

{

printf("连接失败\n");

}

DisconnectNamedPipe(hPipe);

CloseHandle(hPipe);//关闭管道

}

void beginSendThread(PVOID p){

printf("服务器Send\n");

printf("等待连接......\n");

HANDLE hPipe = CreateNamedPipe(toWChar(pStrPipeNameSend), PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);

if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待连接。

{

printf("连接成功,开始发送缓冲区数据至B\n");

while (true)

{

WaitForSingleObject(mutex, INFINITE);

EnterCriticalSection(cs);

WriteFile(hPipe, buf, (int)dwLen, &dwLen, NULL);

LeaveCriticalSection(cs);

Sleep(500);

ReleaseSemaphore(mutex, 1, NULL);

}

}

else

{

printf("连接失败\n");

}

DisconnectNamedPipe(hPipe);

CloseHandle(hPipe);//关闭管道

}

int _tmain(int argc, _TCHAR* argv[])

{

cs = (LPCRITICAL_SECTION)malloc(sizeof(LPCRITICAL_SECTION));

InitializeCriticalSection(cs);

mutex = CreateSemaphore(NULL, 1, 1, TEXT("mutex"));

_beginthread(beginGetThread, NULL, NULL);

_beginthread(beginSendThread, NULL, NULL);

Sleep(INFINITE);

DeleteCriticalSection(cs);

return 0;

}

B程序不断接收从键盘输入的数据,数据以回车结束,并发送给A

#include

#include

#include

#include

const char *pStrPipeName = "\\\\.\\pipe\\Name_pipe_demon_get";

const int BUFFER_MAX_LEN = 1024;

char buf[BUFFER_MAX_LEN];

int _tmain(int argc, _TCHAR* argv[])

{

printf("按任意键以开始连接Get\n");

_getch();

printf("A开始等待......\n");

if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))

{

printf("Error! 连接Get失败\n");

return 0;

}

HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_WRITE, 0,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

while (true)

{

printf("请输入要向服务端发送的数据,回车键结束,最大1024个字节\n");

DWORD dwLen = 0;

int bufSize;

for (bufSize = 0; bufSize < BUFFER_MAX_LEN; bufSize++){

buf[bufSize] = getchar();

if (buf[bufSize] == '\n') break;

}

//向服务端发送数据

if (WriteFile(hPipe, buf, bufSize, &dwLen, NULL)){

printf("数据写入完毕共%d字节\n", dwLen);

}

else

{

printf("数据写入失败\n");

}

Sleep(1000);

}

CloseHandle(hPipe);

return 0;

}

C程序接收到从A发送来的数据,并转换成大写写入文件

#include

#include

#include

#include

const char *pStrPipeName = "\\\\.\\pipe\\Name_pipe_demon_send";

const int BUFFER_MAX_LEN = 1024;

char buf[BUFFER_MAX_LEN];

DWORD dwLen = 0;

int _tmain(int argc, _TCHAR* argv[])

{

printf("按任意键以开始连接Send\n");

_getch();

printf("B开始等待......\n");

if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))

{

printf("Error! 连接Send失败\n");

return 0;

}

HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ, 0,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

while (true)

{

// 接收服务端发回的数据

ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);//读取管道中的内容(管道是一种特殊的文件)

printf("接收服务端发来的信息,长度为%d字节\n", dwLen);

printf("具体数据内容如下:");

HANDLE hWrite = CreateFile(_T("data.txt"), GENERIC_READ | GENERIC_WRITE,

FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

for (int j = 0; j <= (int )dwLen; j++){

putchar(buf[j]);

buf[j] = toupper(buf[j]);

}

putchar('\n');

SetFilePointer(hWrite, NULL, NULL, FILE_END);

WriteFile(hWrite, buf, (int)dwLen, NULL, NULL);

WriteFile(hWrite, "\n", 1, NULL, NULL);

CloseHandle(hWrite);

Sleep(1000);

}

CloseHandle(hPipe);

return 0;

}

🎀 相关推荐

“神”剧,盘点《美国众神》里的各路神仙都有什么来头
C罗248000票当选历史最佳球员:解读这一票选背后的意义_手机网易网
关于中国足球 这里有三个小故事 | 新体谈
365bet足球实时动画

关于中国足球 这里有三个小故事 | 新体谈

📅 06-27 👀 6155