所谓的管道,就是内核⾥⾯的⼀串缓存(Pipe)。一个进程从管道的⼀端写⼊的数据,实际上是缓存在内核中的,另⼀端读取,也就是从内核中读取这段数据。
特性:
匿名管道只能用于父子进程间通信 ,不能跨网络通信,并且通信是单向。另外,管道传输的数据是⽆格式的流且⼤⼩受限。
正常情况下,控制台进程的输输入出是在控制台窗口的,但是如果我们在创建子进程的时候指定了其输入输出,那么子进程就会从我们的管道读数据,把输出数据写到我们指定的管道。
这就是我们代码中重定向标准输入的原因,否则system(“pause”)会无效。
父进程写管道、子进程读管道,过程如下。
这样就做到了两个进程各有一个句柄(父进程写句柄、子进程读句柄),两个进程就可以通过各⾃的句柄 写⼊和读取同⼀个管道⽂件实现跨进程通信。
3.1、创建管道CreatePipe
BOOL CreatePipe(
PHANDLE hReadPipe; //指向管道读句柄
PHANDLE hWritePipe; //指向管道写句柄
LPSECURITY_ATTRIBUTES lpPipeAttributes; //指向管道安全属性
DWORD nSize; //管道大小
)
3.2、写入管道WriteFile
BOOL WriteFile(
HANDLE hFile,//文件句柄
LPCVOID lpBuffer,//数据缓存区指针
DWORD nNumberOfBytesToWrite,//要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针
LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针
);
3.3、读取管道ReadFile
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //接收数据的缓冲区
DWORD nNumberOfBytesToRead, //读取的字节数
LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针
LPOVERLAPPED lpOverlapped
//如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
//该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
);
3.4、获取句柄GetStdHandle
该函数用于取得指定的标准设备的句柄(标准输入,标准输出或标准错误),本文demo用于:子进程获取父进程传递的读句柄。
HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle);
参数 | 含义 |
---|---|
STD_INPUT_HANDLE | 标准输入句柄 |
STD_OUTPUT_HANDLE | 标准输出句柄 |
STD_ERROR_HANDLE | 标准错误句柄 |
//main.cpp
#include
#include "iostream"
#define BUFSIZE 4096
using namespace std;
int main(int argc, char* argv[])
{
cout << "\n ** This is a message from the father process. ** \n";
cout << "第一步:创建管道" << endl;
// Set the bInheritHandle flag so pipe handles are inherited.
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDIN.
HANDLE handle_read;
HANDLE handle_write;
bool ret = CreatePipe(&handle_read, &handle_write, &saAttr, 0);
if (!ret)
{
cout << "创建进程失败:create pipe fail" << endl;
}
//设置写句柄不可以被子进程继承,不设置也不影响。 Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(handle_write, HANDLE_FLAG_INHERIT, 0))
{
cout << "设置句柄失败:set handle fail!" << endl;
}
cout << "第一步:子进程、设置管道句柄的继承" << endl;
{
// Create the child process.
char cmdline[] = "childprocess.exe";
PROCESS_INFORMATION piProcInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure. // This structure specifies the STDIN handles for redirection.
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdInput = handle_read; //把管道的读句柄传给子进程
si.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
ret = CreateProcess(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &piProcInfo);
if (!ret)
cout << "创建子进程失败:create child process faile!";
else
{
cout << "创建子进程成功:create child process sucess!";
// Close handles to the child process and its primary thread.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(handle_read);
}
}
cout << "第三步:向管道中写入数据:write to pipe." << endl;
{
// write contents to the pipe for the child's STDIN.
DWORD len;
char chBuf[BUFSIZE] = " hello pipe";
for (int i = 0; i < 10; i++)
{
bool ret = WriteFile(handle_write, chBuf, sizeof(chBuf), &len, NULL);//子进程读了后,父进程才可以继续写入管道
if (!ret)
{
cout << "写入管道数据失败。i=" << i << endl;
break;
}
else
{
cout << "send data " << i << " is:" << chBuf << endl;
}
}
cout << "写入管道数据结束。" << endl;
// Close the pipe handle so the child process stops reading.
if (!CloseHandle(handle_write))
cout << "colse handle fail" << endl;
}
return 0;
}
// main.cpp
#include
#include"iostream"
#define BUFSIZE 4096
using namespace std;
int main(int argc, char* argv[])
{
cout<<"\n ** This is a message from the child process. ** \n";
CHAR chBuf[BUFSIZE];
DWORD len;
HANDLE handle_read;
handle_read = GetStdHandle(STD_INPUT_HANDLE);
if (handle_read == INVALID_HANDLE_VALUE)
ExitProcess(1);
for (int i = 0;i<5;i++)
{
// Read from standard input and stop on error or no data.
bool ret = ReadFile(handle_read, chBuf, BUFSIZE, &len, NULL);
if (!ret || len == 0)
{
cout << "读取数据失败" << endl;
break;
}
cout << "receive data "<<i<<" is:" << chBuf << endl;
}
cout << "读取数据结束" << endl;
Sleep(5000);
freopen("CON", "r", stdin); // 重定向输入,否则system("pause")会无效
//CloseHandle(handle_read);
system("pause");
return 0;
}
命名管道,顾名思义,这个管道肯定是有名字的。通过管道的名字来确保多个进程访问同一个管道。事实上,命名管道不仅可在同一台计算机的不同进程之间传输数据,甚至能在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
命名管道的服务器和客户机的区别在于:
服务器是唯一一个有权创建命名管道的进程,也只有他才能接受管道客户机的连接请求。
而客户机只能同一个线程的命名管道服务器建立连接。
3.1、创建命名管道CreateNamedPipe
创建命名管道的实例,并返回后续管道操作的句柄。
HANDLE CreateNamedPipeA(
[in] LPCSTR lpName,
[in] DWORD dwOpenMode,
[in] DWORD dwPipeMode,
[in] DWORD nMaxInstances,
[in] DWORD nOutBufferSize,
[in] DWORD nInBufferSize,
[in] DWORD nDefaultTimeOut,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
CreateNamedPipe函数接口中的第一个参数lpName: .\pipe\pipename, 必须为这种格式。中间的“.”表示本地机器,如果要跟远程机器建立连接,则需要设定远程服务器的名字。
3.2、监听请求ConnectNamedPipe
在命名管道实例上监听客户机连接请求。
BOOL ConnectNamedPipe(
[in] HANDLE hNamedPipe,
[in, out, optional] LPOVERLAPPED lpOverlapped
);
3.3、等候一个命名管道实例WaitNamedPipe
等到超时间隔过或指定命名管道的实例可用于连接
BOOL ConnectNamedPipe(
[in] HANDLE hNamedPipe,
[in, out, optional] LPOVERLAPPED lpOverlapped
);
#include
#include
using namespace std;
int main()
{
printf("创建命名管道并等待连接\n");
char pipeName[] = "\\\\.\\Pipe\\mypipe";
HANDLE hPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT
, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);
//waiting to be connected
if (ConnectNamedPipe(hPipe, NULL) != NULL)
{
printf("连接成功,开始发送数据\n");
DWORD dwWrite;
const char* pStr = "data from server";
if (!WriteFile(hPipe, pStr, strlen(pStr), &dwWrite, NULL))
{
cout << "write failed..." << endl << endl;
return 0;
}
cout << "sent data: " << endl << pStr << endl << endl;
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);//关闭管道
printf("关闭管道\n");
system("pause");
}
// ClientPip.cpp
#include
#include
using namespace std;
#define BUFSIZE 5
int main()
{
printf("命名管道:客户端上线\n");
printf("按任意键以开始连接命名管道\n");
getchar();
printf("开始等待命名管道\n");
char pipeName[] = "\\\\.\\Pipe\\mypipe";
if (WaitNamedPipe(pipeName, NMPWAIT_WAIT_FOREVER) == FALSE)
return 0;
printf("打开命名管道\n");
HANDLE hPipe = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if ((long)hPipe == -1)
return 0;
//接收服务端发回的数据
BOOL fSuccess = false;
DWORD len = 0;
char buffer[BUFSIZE];
string recvData = "";
do
{
fSuccess = ReadFile(hPipe, buffer, BUFSIZE * sizeof(char), &len, NULL);
char buffer2[BUFSIZE + 1] = { 0 };
memcpy(buffer2, buffer, len);
recvData.append(buffer2);
if (!fSuccess || len < BUFSIZE)
break;
} while (true);
cout << "recv data:" << endl << recvData.c_str() << endl << endl;
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
system("pause");
return 0;
}
如有错误或不足欢迎评论指出!创作不易,转载请注明出处。如有帮助,记得点赞关注哦(⊙o⊙)
更多内容请关注个人博客:https://blog.csdn.net/qq_43148810