本文使用命名管道的技术,实现回显功能的服务-客户端程序,即客户端发送数据给服务端,服务端原样返回。此处实现的服务端同时只能服务一个客户端,若要服务多个,服务端需要用CreateNamedPipe创建多个命名管道才可以。因为命名管道基于Windows的UNC以及重定向器的技术,使用命名管道写的程序自然就携带了Windows的权限管理功能。
一、回显服务端(阻塞方式)
-
创建命名管道
使用CreateNamedPipe创建命名管道,PIPE_ACCESS_DUPLEX表示双向管道,可读可写,PIPE_TYPE_BYTE表示以字节流方式工作,其他选项参看CreateNamedPipeHANDLE hNamedPipe = CreateNamedPipe(_T("\\\\.\\pipe\\MyNamedPipe"), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, BUFFER_SIZE, BUFFER_SIZE, 0, NULL);
-
监听连接
使用ConnectNamedPipe进行连接的监听,当有客户端连接的时候,此函数就会返回,然后就可以对管道句柄进行读写了::ConnectNamedPipe(hNamedPipe, NULL)
-
接收数据
使用ReadFile等待客户端发来的的数据::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer), &dwReadBytes, NULL)
-
发送数据
使用WriteFile向客户端发送数据::WriteFile(hNamedPipe, szBuffer, dwReadBytes, &dwWriteBytes, NULL)
-
断开连接
使用DisconnectNamedPipe断开管道的连接::DisconnectNamedPipe(hNamedPipe);
二、客户端(阻塞方式)
-
使用WaitNamedPipe等待或查询有没有指定的管道可连接
::WaitNamedPipe(_T("\\\\localhost\\pipe\\MyNamedPipe"), NMPWAIT_WAIT_FOREVER)
-
使用CreateFile正式建立管道的连接,测试了省略第1步,直接CreateFile也是可以成功的,通常要用第1步,否则服务端还没启动好时,CreateFile就会失败,只能不断轮询尝试连接了。
HANDLE hNamedPipe = ::CreateFile(_T("\\\\localhost\\pipe\\MyNamedPipe"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
使用WriteFile向服务端发送数据
if (!::WriteFile(hNamedPipe, strTemp.c_str(), strTemp.size(), &dwWriteBytes, NULL)) { std::cerr << "WriteFile failed with error " << ::GetLastError() << std::endl; break; }
-
使用ReadFile接收数据
if (!::ReadFile(hNamedPipe, szBuffer, _countof(szBuffer) - 1, &dwReadBytes, NULL)) { std::cerr << "ReadFile failed with error " << ::GetLastError() << std::endl; break; }
-
关闭管道句柄
::CloseHandle(hNamedPipe)
三、完整代码
-
服务端
#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; }
-
客户端
#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; }
四、运行截图