进程通信之--匿名管道双向通信
学习了m_buddy 的https://blog.****.net/m_buddy/article/details/72867107 Windows进程通信——匿名管道
发现建了一个匿名管道,双向通信存在问题,建了两个匿名管道
匿名管道是在本地机器上使用,实现父进程和子进程之间的通信的进程通信机制。需要注意两点:
(1)就是在本地机器上,这是因为匿名管道不支持跨网络之间的两个进程之间的通信
(2)实现的是父进程和子进程之间的通信,而不是任意的两个进程,因为需要继承父进程的读写管道句柄
CreatePipe()函数,它的原型为
BOOL WINAPI CreatePipe(
__out PHANDLE hReadPipe, //参数 hReadPipe 为输出参数,该句柄代表管道的读取句柄。
__out PHANDLE hWritePipe, //参数 hWritePipe 为输出参数,该句柄代表管道的写入句柄
__in LPSECURITY_ATTRIBUTES lpPipeAttributes, //参数 lpPipeAttributes 为一个输入参数,指向一个 SECURITY_ATTRIBUTES 的结构体指针
__in DWORD nSize //管道的缓存大小
);
这里由于是父子进程的关系,需要对SECURITY_ATTRIBUTES结构体进行设置,它的原型为
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; //结构体大小
LPVOID lpSecurityDescriptor; //
BOOL bInheritHandle; //由于是父子进程,一定要设置为True,允许继承
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
以下例子实现父子进程通过匿名管道进行双向通信:
父进程核心代码:
void CFatherDlg::my_PipeInit()
{
if (!my_CreatePipe())
return;
if (!my_CreateProcess())
return;
HANDLE hThread = CreateThread(NULL, 0, Pipe_Listen, &m_hPipeRead2, 0, NULL); //创建socket的发送线程
////关闭该接收线程句柄,释放引用计数
CloseHandle(hThread);
}
//************************************************************************
// 函数名称: SetProcessInfo
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 初始化SECURITY_ATTRIBUTES结构体
// 函数参数: PSECURITY_ATTRIBUTES & ps 需要初始化的SECURITY_ATTRIBUTES结构体
// 返 回 值: void
//************************************************************************
void CFatherDlg::SetSecurity_attr(SECURITY_ATTRIBUTES& ps)
{
//这里必须将 bInheritHandle 设置为 TRUE,
//从而使得子进程可以继承父进程创建的匿名管道的句柄
ps.bInheritHandle = TRUE;
ps.lpSecurityDescriptor = NULL;
ps.nLength = sizeof(SECURITY_ATTRIBUTES);
}
//************************************************************************
// 函数名称: SetStartInfo
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 初始化STARTUPINFO结构体
// 函数参数: STARTUPINFO & si 需要初始化的STARTUPINFO结构体
// 返 回 值: void
//************************************************************************
void CFatherDlg::SetStartInfo(STARTUPINFO& si)
{
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESTDHANDLES;//STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
//子进程的标准输入句柄为父进程管道1的读数据句柄
si.hStdInput = m_hPipeRead1; //子进程的标准输入
//子进程的标准输出句柄为父进程管道2的写数据句柄
si.hStdOutput = m_hPipeWrite2;
//子进程的标准错误处理句柄和父进程的标准错误处理句柄一致
si.hStdError = HANDLE(STD_ERROR_HANDLE);
}
//************************************************************************
// 函数名称: my_CreatePipe
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 创建匿名管道
// 返 回 值: bool
//************************************************************************
bool CFatherDlg::my_CreatePipe()
{
SECURITY_ATTRIBUTES sa;
SetSecurity_attr(sa);
//创建管道1
if (!CreatePipe(&m_hPipeRead1, &m_hPipeWrite1, &sa, 0))
{
MessageBox(_T("create anonymous pipe1 failed"));
return false;
}
//创建管道2
if (!CreatePipe(&m_hPipeRead2, &m_hPipeWrite2, &sa, 0))
{
MessageBox(_T("create anonymous pipe2 failed"));
return false;
}
return true;
}
//************************************************************************
// 函数名称: my_SendDataPipe
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 通过管道发送数据
// 函数参数: std::string data 需要发送的数据
// 返 回 值: void
//************************************************************************
void CFatherDlg::my_SendDataPipe(CString data)
{
DWORD data_write;
//写入数据
if (!WriteFile(m_hPipeWrite1, (LPCTSTR)data, data.GetLength() * 2, &data_write, NULL))
{
MessageBox(_T("Father send data Failed!"));
}
}
//************************************************************************
// 函数名称: my_CreateProcess
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 创建进程
// 返 回 值: bool
//************************************************************************
bool CFatherDlg::my_CreateProcess()
{
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si;
SetStartInfo(si);
LPWSTR exe_path = _T("E:\\yebo\\study\\C&C++\\Test\\ProcessTX\\Process\\Son\\Debug\\Son.exe");
BOOL kk = CreateProcess(exe_path, NULL, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi);
if (!kk)
{
MessageBox(_T("create process failed"));
CloseHandle(m_hPipeWrite1); //创建进程失败,关闭读写句柄
CloseHandle(m_hPipeRead1);
CloseHandle(m_hPipeWrite2); //创建进程失败,关闭读写句柄
CloseHandle(m_hPipeRead2);
m_hPipeWrite1 = NULL;
m_hPipeRead1 = NULL;
m_hPipeWrite2 = NULL;
m_hPipeRead2 = NULL;
return false;
}
//由于结构体pi中的数据已经使用不到了,将其资源释放
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
//************************************************************************
// 函数名称: Pipe_Listen
// 访问权限: public
// 创建日期: 2017/06/05
// 创 建 人:
// 函数说明: 监听管道的数据
// 函数参数: LPVOID lpParameter
// 返 回 值: DWORD WINAPI
//************************************************************************
DWORD WINAPI CFatherDlg::Pipe_Listen(LPVOID lpParameter)
{
HANDLE* PipeRead = (HANDLE*)lpParameter;
TCHAR data[4096] = { 0 };
DWORD data_read;
while (true)
{
memset(data, 0, sizeof(data));
if (!ReadFile((HANDLE)(*PipeRead), data, 4096, &data_read, NULL))
continue;
CString str(_T("儿子说:"));
AfxGetApp()->m_pMainWnd->GetDlgItemText(IDC_EDIT_Recv, str);
str += _T("儿子说:") + (CString)data + _T("\r\n");
AfxGetApp()->m_pMainWnd->SetDlgItemText(IDC_EDIT_Recv, str);
Sleep(100);
}
return data_read;
}
void CFatherDlg::OnBnClickedButtonSend()
{
// TODO: 在此添加控件通知处理程序代码/
CString str;
GetDlgItemText(IDC_EDIT_Send, str);
my_SendDataPipe(str);
}
子进程核心代码:
void CSonDlg::my_PipeInit()
{
if (!my_GetParentPipeHandle())
return; //初始化管道
HANDLE hThread = CreateThread(NULL, 0, Pipe_Listen, &m_hPipeRead, 0, NULL); //创建socket的发送线程
////关闭该接收线程句柄,释放引用计数
CloseHandle(hThread);
}
bool CSonDlg::my_GetParentPipeHandle()
{
m_hPipeWrite = GetStdHandle(STD_OUTPUT_HANDLE);
m_hPipeRead = GetStdHandle(STD_INPUT_HANDLE );
if (INVALID_HANDLE_VALUE == m_hPipeRead || INVALID_HANDLE_VALUE == m_hPipeWrite)
{
MessageBox(_T("从父进程获得管道读写句柄失败"));
CloseHandle(m_hPipeRead);
CloseHandle(m_hPipeWrite);
return false;
}
return true;
}
void CSonDlg::my_SendDataPipe(CString data)
{
DWORD data_write;
//写入数据
if (!WriteFile(m_hPipeWrite, (LPCTSTR)data, data.GetLength() * 2, &data_write, NULL))
{
MessageBox(_T("发送信息失败"));
}
}
DWORD WINAPI CSonDlg::Pipe_Listen(LPVOID lpParameter) //接收管道数据线程
{
HANDLE* PipeRead = (HANDLE*)lpParameter;
TCHAR data[4096] = { 0 };
DWORD data_read;
while (true)
{
memset(data, 0, sizeof(data));
if (!ReadFile((HANDLE)(*PipeRead), data, 4096, &data_read, NULL))
continue;
CString str(_T("爸爸说:"));
AfxGetApp()->m_pMainWnd->GetDlgItemText(IDC_EDIT_Recv, str);
str += _T("爸爸说:") + (CString)data + _T("\r\n");
AfxGetApp()->m_pMainWnd->SetDlgItemText(IDC_EDIT_Recv, str);
//Sleep(200);
}
return data_read;
}
void CSonDlg::OnBnClickedButtonSend()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
GetDlgItemText(IDC_EDIT_Send, str);
my_SendDataPipe(str);
}
内核的两个匿名管道示意图如下:
si.hStdInput = m_hPipeRead1; //子进程的标准输入
si.hStdOutput = m_hPipeWrite2; //子进程的标准输出
运行结果如下: