命名管道的基本用法

本文使用命名管道的技术,实现回显功能的服务-客户端程序,即客户端发送数据给服务端,服务端原样返回。此处实现的服务端同时只能服务一个客户端,若要服务多个,服务端需要用CreateNamedPipe创建多个命名管道才可以。因为命名管道基于Windows的UNC以及重定向器的技术,使用命名管道写的程序自然就携带了Windows的权限管理功能。

一、回显服务端(阻塞方式)

  1. 创建命名管道
    使用CreateNamedPipe创建命名管道,PIPE_ACCESS_DUPLEX表示双向管道,可读可写,PIPE_TYPE_BYTE表示以字节流方式工作,其他选项参看CreateNamedPipe

    HANDLE hNamedPipe = CreateNamedPipe(_T("\\\\.\\pipe\\MyNamedPipe"),
    		PIPE_ACCESS_DUPLEX, 
    		PIPE_TYPE_BYTE, 
    		PIPE_UNLIMITED_INSTANCES, 
    		BUFFER_SIZE, 
    		BUFFER_SIZE, 0, NULL);
    
  2. 监听连接
    使用ConnectNamedPipe进行连接的监听,当有客户端连接的时候,此函数就会返回,然后就可以对管道句柄进行读写了

    ::ConnectNamedPipe(hNamedPipe, NULL)
    
  3. 接收数据
    使用ReadFile等待客户端发来的的数据

    ::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer), &dwReadBytes, NULL)
    
  4. 发送数据
    使用WriteFile向客户端发送数据

    ::WriteFile(hNamedPipe, szBuffer, dwReadBytes, &dwWriteBytes, NULL)
    
  5. 断开连接
    使用DisconnectNamedPipe断开管道的连接

    ::DisconnectNamedPipe(hNamedPipe);
    

二、客户端(阻塞方式)

  1. 使用WaitNamedPipe等待或查询有没有指定的管道可连接

    ::WaitNamedPipe(_T("\\\\localhost\\pipe\\MyNamedPipe"), NMPWAIT_WAIT_FOREVER)
    
  2. 使用CreateFile正式建立管道的连接,测试了省略第1步,直接CreateFile也是可以成功的,通常要用第1步,否则服务端还没启动好时,CreateFile就会失败,只能不断轮询尝试连接了。

    HANDLE hNamedPipe = ::CreateFile(_T("\\\\localhost\\pipe\\MyNamedPipe"),
    		GENERIC_READ | GENERIC_WRITE, 0,
    		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
  3. 使用WriteFile向服务端发送数据

    if (!::WriteFile(hNamedPipe, strTemp.c_str(), strTemp.size(), &dwWriteBytes, NULL))
    {
    	std::cerr << "WriteFile failed with error " << ::GetLastError() << std::endl;
    	break;
    }
    
  4. 使用ReadFile接收数据

    if (!::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer) - 1, &dwReadBytes, NULL))
    {
    	std::cerr << "ReadFile failed with error " << ::GetLastError() << std::endl;
    	break;
    }
    
  5. 关闭管道句柄

    ::CloseHandle(hNamedPipe)
    

三、完整代码

  1. 服务端

    #include <windows.h>
    #include <iostream>
    #include <tchar.h>
    
    #define BUFFER_SIZE 256
    
    int main(int argc, char** argv)
    {
    	HANDLE hNamedPipe = CreateNamedPipe(_T("\\\\.\\pipe\\MyNamedPipe"),
    		PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, BUFFER_SIZE, BUFFER_SIZE, 0, NULL);
    	if (hNamedPipe == INVALID_HANDLE_VALUE)
    	{
    		std::cerr << "create named pipe failed with error " << ::GetLastError() << std::endl;
    		return -1;
    	}
    
    	bool bExit = false;
    	do 
    	{
    		std::cout << "wait connect ..." << std::endl;
    		if (!::ConnectNamedPipe(hNamedPipe, NULL))
    		{
    			std::cerr << "ConnectNamedPipe failed with error " << ::GetLastError() << std::endl;
    			break;
    		}
    
    		std::cout << "client connected" << std::endl;
    
    		do
    		{
    			CHAR szBuffer[256] = { 0 };
    			DWORD dwReadBytes = 0;
    			if (!::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer), &dwReadBytes, NULL))
    			{
    				std::cerr << "ReadFile failed with error " << ::GetLastError() << std::endl;
    				break;
    			}
    			std::cout << "ReadFile: " << szBuffer << std::endl;
    
    			if (strcmp(szBuffer, "exit") == 0)
    			{
    				bExit = true;
    				break;
    			}
    
    			DWORD dwWriteBytes = 0;
    			if (!::WriteFile(hNamedPipe, szBuffer, dwReadBytes, &dwWriteBytes, NULL))
    			{
    				std::cerr << "WriteFile failed with error " << ::GetLastError() << std::endl;
    				break;
    			}
    		} while (true);
    
    		//必须调用
    		//A named pipe server process can use ConnectNamedPipe with a newly created pipe instance. 
    		//It can also be used with an instance that was previously connected to another client process; 
    		//in this case, the server process must first call the DisconnectNamedPipe function to disconnect 
    		//the handle from the previous client before the handle can be reconnected to a new client. 
    		//Otherwise, ConnectNamedPipe returns FALSE, and GetLastError returns ERROR_NO_DATA if 
    		//the previous client has closed its handle or ERROR_PIPE_CONNECTED if it has not closed its handle.
    		::DisconnectNamedPipe(hNamedPipe);
    
    	} while (!bExit);	
    	::CloseHandle(hNamedPipe);
    	return 0;
    }
    
  2. 客户端

    #include <windows.h>
    #include <iostream>
    #include <tchar.h>
    #include <string>
    
    int main(int argc, char** argv)
    {
    	if (!::WaitNamedPipe(_T("\\\\localhost\\pipe\\MyNamedPipe"), NMPWAIT_WAIT_FOREVER))
    	{
    		std::cerr << "WaitNamedPipe failed with error " << ::GetLastError() << std::endl;
    		return -1;
    	}
    
    	HANDLE hNamedPipe = ::CreateFile(_T("\\\\localhost\\pipe\\MyNamedPipe"),
    		GENERIC_READ | GENERIC_WRITE, 0,
    		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    	if (INVALID_HANDLE_VALUE == hNamedPipe)
    	{
    		std::cerr << "CreateFile failed with error " << ::GetLastError() << std::endl;
    		return -1;
    	}
    
    	std::string strTemp;
    	while (getline(std::cin, strTemp))
    	{
    		DWORD dwWriteBytes = 0;
    		if (!::WriteFile(hNamedPipe, strTemp.c_str(), strTemp.size(), &dwWriteBytes, NULL))
    		{
    			std::cerr << "WriteFile failed with error " << ::GetLastError() << std::endl;
    			break;
    		}
    
    		CHAR szBuffer[256] = { 0 };
    		DWORD dwReadBytes = 0;
    		if (!::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer) - 1, &dwReadBytes, NULL))
    		{
    			std::cerr << "ReadFile failed with error " << ::GetLastError() << std::endl;
    			break;
    		}
    
    		std::cout << "ReadFile: " << szBuffer << std::endl;
    	}
    
    	::CloseHandle(hNamedPipe);
    
    	return 0;
    }
    

四、运行截图

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值