管道通信相关知识总结

管道

概念

管道是进程间通信使用的一种共享内存的方式,创建管道的进程称为管道服务器,连接管道的进程成为管道客户端。进程间通过管道方式,一个进程往管道写入数据,另一个进程从管道取数据,类似socket通信从buf缓冲区读写数据,管道操作也类似文件读写。管道分为匿名管道和命名管道,文中主要介绍命名管道操作。

匿名管道

匿名管道,没有名称,而且是单工模式,就是两个进程只能一个进程往管道写入数据,另一个进程往管道区数据,而且匿名管道只能使用在父进程与子进程之间,因此如果想要在局域网内使用管道通信,不能使用匿名管道。

命名管道

命名管道,有名称,服务器端有个进程负责创建一个有名的管道,并且可以接收客户端的连接请求,客户端只能连接给定的管道名称,命名管道提供两种通信模式,字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位进行数据的收发,每次在管道上发出一条消息后,它必须作为一条完整的消息读入。

服务器API接口

HANDLE CreateNamedPipeA(
  LPCSTR                lpName,   
  DWORD                 dwOpenMode,
  DWORD                 dwPipeMode,
  DWORD                 nMaxInstances,
  DWORD                 nOutBufferSize,
  DWORD                 nInBufferSize,
  DWORD                 nDefaultTimeOut,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

lpName管道名称,格式为 “\\.\pipe\pipename”,pipename不区分大小写,如果是远程终端进程间通信,格式中的’.'可以替换成ip地址,此时需要降低进程访问的权限,否者客户端进程在与服务器进程连接时报用户名或密码错误,或者权限不够,错误码1326或者5.

dwOpenMode打开模式,指定管道访问模式,PIPE_ACCESS_DUPLEX客户端和服务器都能读写管道数据,PIPE_ACCESS_INBOUND客户端写,服务器读取数据,PIPE_ACCESS_OUTBOUND客户端读取,服务器写入数据。

dwPipeMode管道模式,分为字节模式和消息模式,这两种模式和实例模式,等待模式结合使用,如果需要指定是否需要远程客户端过来连接,模式参数为PIPE_ACCEPT_REMOTE_CLIENTS PIPE_REJECT_REMOTE_CLIENTS

nMaxInstances管道实例创建的最大数量,最大值为PIPE_UNLIMITED_INSTANCES,如果超过这个值,则返回INVALID_HANDLE_VALUE

nOutBufferSize管道输出buffer的大小

nInBufferSize管道输入buffer的大小

nDefaultTimeOut默认超时时间,单位是毫秒,如果不设置,可以置为FILE_ATTRIBUTE_NORMAL

lpSecurityAttributes安全属性结构指针,默认设置为NULL,如果需要降低进程访问权限,需要设置安全属性结构。

BOOL ConnectNamedPipe(
  HANDLE       hNamedPipe,
  LPOVERLAPPED lpOverlapped
);

等待客户端建立连接,类似TCP通信的accept函数。

hNamedPipe是管道句柄。

lpOverlapped是指向重叠结构的指针,设置为NULL。

BOOL ReadFile(
  HANDLE       hFile,
  LPVOID       lpBuffer,
  DWORD        nNumberOfBytesToRead,
  LPDWORD      lpNumberOfBytesRead,
  LPOVERLAPPED lpOverlapped
);

从管道缓冲buffer读取数据

BOOL WriteFile( 
  HANDLE hFile, 
  LPCVOID lpBuffer, 
  DWORD nNumberOfBytesToWrite, 
  LPDWORD lpNumberOfBytesWritten, 
  LPOVERLAPPED lpOverlapped
);

向管道缓冲buffer写入数据

BOOL DisconnectNamedPipe(
HANDLE hNamedPipe
// handle to named pipe
);

断开客户端的连接

BOOL
WINAPI
CloseHandle(
    _In_ _Post_ptr_invalid_ HANDLE hObject
    );

关闭管道句柄

客户端API接口

BOOL WaitNamedPipeA(
  LPCSTR lpNamedPipeName,
  DWORD  nTimeOut
);

判断命名管道是否存在并且可用,设置超时时间。

lpNamedPipeName命名管道名称,如果是本机环境,与服务器一致,如果是远程终端进程通信,需要把 "."换成远程服务器ip,格式 \\servername\pipe\pipename

nTimeOut超时时间。

HANDLE CreateFileA(
  LPCSTR                lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile
);

CreateFileA类似tcp通信客户端的connect接口,与服务器通信。

lpFileName管道名称

dwDesiredAccess访问权限,一般设置读或写,或者读与写

dwShareMode共享模式,就是文件被打开时,其它进程或者线程是否可以操作,例如共享读写,如果设置为NULL,其它进程只能等到文件句柄关闭时才可以操作。

lpSecurityAttributes安全属性结构指针,设置为NULL

dwCreationDisposition文件是否存在,一般设置OPEN_EXISTING

dwFlagsAndAttributes一般设置FILE_ATTRIBUTE_NORMAL

hTemplateFile如果打开一个存在的文件,此参数被忽略,一般设置NULL

BOOL SetNamedPipeHandleState(
  HANDLE  hNamedPipe,
  LPDWORD lpMode,
  LPDWORD lpMaxCollectionCount,
  LPDWORD lpCollectDataTimeout
);

对命名管道设置读写模式和阻塞模式。

读写数据同上

服务器端示例代码

#include <windows.h>
#include <iostream>

using namespace std;

#define BUFSIZE 4096
#pragma comment(lib, "Advapi32.lib")

int main()
{
	int nRecv, nSend;
	DWORD dwBytes = 0;
	char cRecv[BUFSIZE] = { 0x00 };
	char cSend[BUFSIZE] = {0x00};
	HANDLE hNamedPipe;
	LPSTR lpsPipeName = (char*)"\\\\.\\pipe\\testNamePipe";



	BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
	SECURITY_ATTRIBUTES sa;

	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = &sd;

	InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
	SetSecurityDescriptorDacl(&sd, TRUE, (PACL)0, FALSE);

	hNamedPipe = CreateNamedPipeA(
		lpsPipeName,
		PIPE_ACCESS_DUPLEX,
		PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT| PIPE_ACCEPT_REMOTE_CLIENTS,
		PIPE_UNLIMITED_INSTANCES,
		BUFSIZE,
		BUFSIZE,
		FILE_ATTRIBUTE_NORMAL,
		&sa
	);

	if (hNamedPipe == INVALID_HANDLE_VALUE)
	{
		cout << "create pipe failed.\n";
		return 0;
	}
	cout << "hNamedPipe value:" << hNamedPipe << endl;
	bool bConnect = ConnectNamedPipe(hNamedPipe, NULL);
	if (bConnect)
	{
		memset(cRecv, 0x00, sizeof(char)*BUFSIZE);
		bool bRet = ReadFile(
			hNamedPipe,
			cRecv,
			BUFSIZE*sizeof(char),
			&dwBytes,
			NULL
		);
		if (bRet)
		{
			cout<< "recv data len:" << dwBytes << endl;
			cout << "recv data: "<<cRecv << endl;
		}
		else
		{
			cout << "recv data failed" << endl;
		}
		memset(cSend, 0x00, sizeof(char)*BUFSIZE);
		strcpy_s(cSend, "test pipe ok");
		bRet = WriteFile(hNamedPipe, cSend, strlen(cSend), &dwBytes, NULL);
		if (!bRet || dwBytes != strlen(cSend))
		{
			FlushFileBuffers(hNamedPipe);
			DisconnectNamedPipe(hNamedPipe);
			CloseHandle(hNamedPipe);
		}
	}
	else
	{
		CloseHandle(hNamedPipe);
	}
	return 1;
}

客户端示例代码

#include <windows.h>
#include <iostream>

using namespace std;
#define BUFSIZE 4096

int main()
{
	HANDLE hNamedPipe;
	int nRecv, nSend;
	DWORD dwBytes = 0, dwMode;
	char cRecv[BUFSIZE] = { 0x00 };
	char cSend[BUFSIZE] = { 0x00 };
	LPSTR lpsPipeName = (char*)"\\\\192.168.6.50\\pipe\\testNamePipe";

	if (!WaitNamedPipeA(lpsPipeName, 20000))
	{
		cout << "can not open pipe" << GetLastError() << endl;
		return 0;
	}

	hNamedPipe = CreateFileA(
		lpsPipeName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
	);
	if (hNamedPipe == INVALID_HANDLE_VALUE)
	{
		cout << "hNamedPipe is not valid." << GetLastError() << endl;
		return 0;
	}
	if (GetLastError() == ERROR_PIPE_BUSY)
	{
		cout << "can not open pipe, busy" << endl;
		return 0;
	}
	cout << "hNamedPipe value:" << hNamedPipe << endl;
	dwMode = PIPE_READMODE_MESSAGE| PIPE_ACCEPT_REMOTE_CLIENTS;
	bool bRet = SetNamedPipeHandleState(hNamedPipe, &dwMode, NULL, NULL);
	if (!bRet)
	{
		cout << "SetNamedPipeHandleState failed." << endl;
		return 0;
	}
	strcpy_s(cSend, "test pipe communication.");
	bRet = WriteFile(hNamedPipe, cSend, strlen(cSend), &dwBytes, NULL);
	if (!bRet)
	{
		cout << "WriteFile failed. GetLastError() " << GetLastError() << endl;
		return 0;
	}
	bRet = ReadFile(hNamedPipe, cRecv, BUFSIZE*sizeof(char), &dwBytes, NULL);
	if (!bRet)
	{
		cout << "ReadFile failed. GetLastError() " << GetLastError() << endl;
		return 0;
	}
	CloseHandle(hNamedPipe);
	return 1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

feng_blog6688

只需一个赞,谢谢你的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值