命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。
将命名管道作为一种网络编程方案时,它实际上建立了一个C/S通信体系,并在其中可靠的传输数据。命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。命名管道提供了两种基本通信模式,字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位进行数据的收发,每次在管道上发出一条消息后,它必须作为一条完整的消息读入。
0x01 Server端
代码流程:
1、创建命名管道:CreateNamedPipe
2、等待客户端连接:ConnectNamedPipe
3、读取客户端请求数据:ReadFile
4、向客户端回复数据:WriteFile
5、关闭连接:DisconnectNamedPipe
6、关闭管道:CloseHandle
使用函数:
CreateNamedPipe创建一个命名的管道
HANDLE CreateNamedPipe( LPCTSTR lpName, // 管道名称,形式必须为\\.\pipe\pipeName DWORD dwOpenMode, // 打开管道的模式 DWORD dwPipeMode, // 管道的模式,传输数据的形式 DWORD nMaxInstances, // 最大连接客户端的个数 DWORD nOutBufferSize, // 输出缓冲区的大小 DWORD nInBufferSize, // 输入缓冲区的大小 DWORD nDefaultTimeOut, // 默认的超时时间 LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性,一般为NULL );
ConnectNamedPipe等待一个客户端的连接
BOOL ConnectNamedPipe( HANDLE hNamedPipe, // 命名管道对象 LPOVERLAPPED lpOverlapped // OVERLAPPED结构 );
通信的操作跟我们的文件操作是一样的,通过ReadFile和WriteFile来进行读和写。
通信完之后,调用DisconnectNamedPipe来进行断开连接
BOOL DisconnectNamedPipe( HANDLE hNamedPipe // 命名管道对象 );
Server端源码:
1 // NamedPipeServer.cpp : 定义控制台应用程序的入口点。 2 // 3 #include "stdafx.h" 4 #include <windows.h> 5 #include <strsafe.h> 6 7 #define BUFSIZE 4096 8 9 DWORD WINAPI InstanceThread(LPVOID); 10 VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD); 11 12 int _tmain(VOID) 13 { 14 BOOL fConnected; 15 DWORD dwThreadId; 16 HANDLE hPipe, hThread; 17 LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\LiudadaNemaedPipe"); 18 19 // The main loop creates an instance of the named pipe and 20 // then waits for a client to connect to it. When the client 21 // connects, a thread is created to handle communications 22 // with that client, and the loop is repeated. 23 24 for (;;) 25 { 26 hPipe = CreateNamedPipe( 27 lpszPipename, // pipe name 指向管道名称的指针 28 PIPE_ACCESS_DUPLEX, // read/write access 管道打开模式 29 PIPE_TYPE_MESSAGE | // message type pipe 管道模式 30 PIPE_READMODE_MESSAGE | // message-read mode 31 PIPE_WAIT, // blocking mode 32 PIPE_UNLIMITED_INSTANCES, // max. instances 最大实例数 33 BUFSIZE, // output buffer size 输出缓存大小 34 BUFSIZE, // input buffer size 输入缓存大小 35 0, // client time-out 超时设置 36 NULL); // default security attribute 37 38 if (hPipe == INVALID_HANDLE_VALUE) 39 { 40 printf("CreatePipe failed"); 41 return 0; 42 } 43 44 // Wait for the client to connect; if it succeeds, 45 // the function returns a nonzero value. If the function 46 // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 47 48 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 49 50 if (fConnected) 51 { 52 // Create a thread for this client. 53 hThread = CreateThread( 54 NULL, // no security attribute 55 0, // default stack size 56 InstanceThread, // thread proc 57 (LPVOID)hPipe, // thread parameter 58 0, // not suspended 59 &dwThreadId); // returns thread ID 60 61 if (hThread == NULL) 62 { 63 printf("CreateThread failed"); 64 return 0; 65 } 66 else CloseHandle(hThread); 67 } 68 else 69 { 70 // The client could not connect, so close the pipe. 71 CloseHandle(hPipe); 72 } 73 } 74 return 1; 75 } 76 77 DWORD WINAPI InstanceThread(LPVOID lpvParam) 78 { 79 TCHAR chRequest[BUFSIZE]; 80 TCHAR chReply[BUFSIZE]; 81 DWORD cbBytesRead, cbReplyBytes, cbWritten; 82 BOOL fSuccess; 83 HANDLE hPipe; 84 85 // The thread‘s parameter is a handle to a pipe instance. 86 87 hPipe = (HANDLE)lpvParam; 88 89 while (1) 90 { 91 // Read client requests from the pipe. 92 fSuccess = ReadFile( 93 hPipe, // handle to pipe 94 chRequest, // buffer to receive data 95 BUFSIZE * sizeof(TCHAR), // size of buffer 96 &cbBytesRead, // number of bytes read 97 NULL); // not overlapped I/O 98 99 if (!fSuccess || cbBytesRead == 0) 100 break; 101 printf((const char*)chRequest); 102 103 GetAnswerToRequest(chRequest, chReply, &cbReplyBytes); 104 105 // Write the reply to the pipe. 106 fSuccess = WriteFile( 107 hPipe, // handle to pipe 108 chReply, // buffer to write from 109 cbReplyBytes, // number of bytes to write 110 &cbWritten, // number of bytes written 111 NULL); // not overlapped I/O 112 113 if (!fSuccess || cbReplyBytes != cbWritten) break; 114 } 115 116 // Flush the pipe to allow the client to read the pipe‘s contents 117 // before disconnecting. Then disconnect the pipe, and close the 118 // handle to this pipe instance. 119 120 FlushFileBuffers(hPipe); 121 DisconnectNamedPipe(hPipe); 122 CloseHandle(hPipe); 123 124 return 1; 125 } 126 127 VOID GetAnswerToRequest(LPTSTR chRequest, 128 LPTSTR chReply, LPDWORD pchBytes) 129 { 130 _tprintf(TEXT("%s\n"), chRequest); 131 StringCchCopy(chReply, BUFSIZE, TEXT("Message from server")); 132 *pchBytes = (lstrlen(chReply) + 1) * sizeof(TCHAR); 133 }
NamedPipeServer
0x01 Client端
代码流程:
1、打开命名管道:CreateFile
2、等待服务端响应:WaitNamedPipe
3、切换管道为读模式:SetNamedPipeHandleState
4、向服务端发数据:WriteFile
5、读服务端返回的数据:ReadFile
6、关闭管道:CloseHandle
WaitNamedPipe来检查一下,命名管道是否存在:
BOOL WaitNamedPipe( LPCTSTR lpNamedPipeName, // 管道名称,形式必须为<span style="font-family: Arial, Helvetica, sans-serif;">\\.\pipe\pipeName</span> DWORD nTimeOut // 超时时间,给NULL为默认的超时时间 );
SetNamedPipeHandleState 设置管道的一些参数,传输模式
BOOL WINAPI SetNamedPipeHandleState( _In_ HANDLE hNamedPipe,//命名管道句柄 _In_opt_ LPDWORD lpMode,//传输模式,字节流或者信息流 _In_opt_ LPDWORD lpMaxCollectionCount,//最大字节数 _In_opt_ LPDWORD lpCollectDataTimeout//超时 );
Client端源码:
1 // NamedPipeClient.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6 #include <conio.h> 7 #define BUFSIZE 512 8 9 int main(int argc, TCHAR *argv[]) 10 { 11 HANDLE hPipe; 12 LPTSTR lpvMessage = TEXT("Message from client"); 13 TCHAR chBuf[BUFSIZE]; 14 BOOL fSuccess; 15 DWORD cbRead, cbWritten, dwMode; 16 LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\LiudadaNemaedPipe"); 17 18 if (argc > 1) 19 lpvMessage = argv[1]; 20 21 // Try to open a named pipe; wait for it, if necessary. 22 23 while (1) 24 { 25 hPipe = CreateFile( 26 lpszPipename, // pipe name 27 GENERIC_READ | // read and write access 28 GENERIC_WRITE, 29 0, // no sharing 30 NULL, // default security attributes 31 OPEN_EXISTING, // opens existing pipe 32 0, // default attributes 33 NULL); // no template file 34 35 // Break if the pipe handle is valid. 36 37 if (hPipe != INVALID_HANDLE_VALUE) 38 break; 39 40 // Exit if an error other than ERROR_PIPE_BUSY occurs. 41 42 if (GetLastError() != ERROR_PIPE_BUSY) 43 { 44 printf("Could not open pipe"); 45 return 0; 46 } 47 48 // All pipe instances are busy, so wait for 20 seconds. 49 50 if (!WaitNamedPipe(lpszPipename, 20000)) 51 { 52 printf("Could not open pipe"); 53 return 0; 54 } 55 } 56 57 // The pipe connected; change to message-read mode. 58 59 dwMode = PIPE_READMODE_MESSAGE; 60 fSuccess = SetNamedPipeHandleState( 61 hPipe, // pipe handle 62 &dwMode, // new pipe mode 63 NULL, // don‘t set maximum bytes 64 NULL); // don‘t set maximum time 65 if (!fSuccess) 66 { 67 printf("SetNamedPipeHandleState failed"); 68 return 0; 69 } 70 71 // Send a message to the pipe server. 72 73 fSuccess = WriteFile( 74 hPipe, // pipe handle 75 lpvMessage, // message 76 (lstrlen(lpvMessage) + 1) * sizeof(TCHAR), // message length 77 &cbWritten, // bytes written 78 NULL); // not overlapped 79 if (!fSuccess) 80 { 81 printf("WriteFile failed"); 82 return 0; 83 } 84 85 do 86 { 87 // Read from the pipe. 88 89 fSuccess = ReadFile( 90 hPipe, // pipe handle 91 chBuf, // buffer to receive reply 92 BUFSIZE * sizeof(TCHAR), // size of buffer 93 &cbRead, // number of bytes read 94 NULL); // not overlapped 95 96 if (!fSuccess && GetLastError() != ERROR_MORE_DATA) 97 break; 98 99 _tprintf(TEXT("%s\n"), chBuf); 100 //printf("%S\n", chBuf); 101 } while (!fSuccess); // repeat loop if ERROR_MORE_DATA 102 103 getch(); 104 105 CloseHandle(hPipe); 106 107 return 0; 108 }
NamedPipeClient