Windows管道技术
LPC机制是在Windows进程之间的高效的通信手段。Windows命名管道是一种可用于不同系统之间进行网络通信的机制。命名管道提供了双向通信的能力,与Windows的文件对象管理紧密的连接在一起。
命名管道有名称,服务器在创建一个命名管道的时候指定该管道的名称。客户通过名称连接到该管道上。名称是服务器和客户之间的联系纽带。
Windows中命名管道的名称规范是UNC(Universal Naming Convention)。管道的命名格式为:
"\\<Server>\Pipe\<PipeName>"
- Server指定了一个命名管道的服务器所在的计算机的名称可以是DNS名称或字符串形式的IP地址;
- PipeName是该命名管道的唯一名,这一部分可以是层次结构中的一个全路径名,允许包含多级目录。
eg:
\\MyServer\pipe\MyAppServer\connectionPipe
命名管道的通信模型
命名管道与LPC一样都是通过连接的方式进行的,服务器创建一个命名管道对象,然后在此对象上等待请求连接。一旦客户请求连接过来,两者可以通过命名管道读取或写入数据,与LPC不同的是,命名管道实际上使用的是文件接口,他的实现使用了文件系统驱动程序的形式并且支持远程通信。
服务器进程通过调用API函数CreateNamedPipe来创建一个有名称的命名管道,必须指定一个本地命名管道名称。Windows允许同一个名称有多个命名管道实例。所以,服务器进程在调用CreateNamedPipe函数时必须指定最大允许的实例数(1-255)。该函数返回以后,服务器进程得到一个指向命名管道实例的句柄。服务器进程调用API函数ConnectNamePipe函数等待用户的连接请求。
HANDLE
WINAPI
CreateNamedPipeA(LPCSTR lpName,
DWORD dwOpenMode,
DWORD dwPipeMode,
DWORD nMaxInstances,
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
BOOL
WINAPI
ConnectNamedPipe(IN HANDLE hNamedPipe,
IN LPOVERLAPPED lpOverlapped);
用户端进程调用CreatFile函数连接到一个正在等待连接的命名管道上,由于客户指定了命名管道的名称,所以内核中的对象管理器会把此请求转给MUP或交给本机的命名管道文件系统驱动程序ntfs.sys,当CreateFile返回后用户得到一个指向已经建立连接的命名管道实例的句柄。至此服务器进程ConnectNamedPipe函数也已经完成其建立连接的任务。
每个建立连接的命名管道实例总是伴随着一对服务进程和客户进程。在这两个进程中分别有一个句柄指向该管道。
服务器进程调用DisconnectNamePipe函数将管道实例与用户进程断开从而可以重新连接到新的客户进程。用户进程和客户进程都可以调用CloseHandle来关闭一个建立连接的命名管道实例。
WaitNamedPipe函数可以检测是否该管道调用了ConnectNamedPipe函数等待连接。如果。如果返回非0值客户进程调用CreateFile函数来创建命名管道实例。
服务器和客户进程建立了命名管道连接,就可以通过文件接口来读写该命名管道的实例,即调用ReadFile/WriteFile从管道读或向管道写数据。
一旦建立连接后,服务器和客户进程都可以调用SetNamedPipeHandleState函数来设置命名管道实例的读操作模式和等待模式。命名管道支持字节模式和消息模式,服务器进程在创建命名管道时指定它的默认模式。一旦服务器指定了管道为字节模式,则客户端不能设置消息模式。如果服务器设置了消息模式,客户端允许设置为字节模式。
两个函数:
- TranscatNamedPipe:
- 用于消息模式的命名管道,将一个写消息和一个读消息操作合并为一个网络操作,从而提高客户与远程服务之间的网络通信性能。客户进程和服务进程都可以使用TransactNamedPipe函数
- CallNamePipe:
- 仅用于客户进程。该函数功能等价于先调用CreateFile函数建立命名管道实例连接。然后调用TransactNamedPipe函数来发送和接收消息,最后调用CloseHandle函数来关闭命名管道实例。
命名管道的实现
仅讨论命名管道服务器和客户进程位于本地的情形。客户进程连接的是一个本地的命名管道,对象管理器在解析该命名管道的时候无序触及MUP驱动程序。
服务器创建命名管道的过程:
API函数CreateNamedPipe将创建命名管道的请求交给内核层的NtCreateNamedPipe函数,该函数接受命名管道的名称"\??PIPE\<PipeName>"。然后NtCreateNamedPipe函数只是将参数组织到一个数据结构NAMED_PIPE_CREATE_PARAMETERS中,然后调用函数IoCreateFile函数来完成创建任务。IoCreateFile有一个CREATE_FILE_TYPE参数指明要创建的类型:普通的文件,命名管道或者邮件槽。IoCreateFIle调用函数IopCreateFile函数调用对象管理器的ObOpenObjectByName函数来完成创建工作。
NTSTATUS
NTAPI
NtCreateNamedPipeFile(OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN ULONG NamedPipeType,
IN ULONG ReadMode,
IN ULONG CompletionMode,
IN ULONG MaximumInstances,
IN ULONG InboundQuota,
IN ULONG OutboundQuota,
IN PLARGE_INTEGER DefaultTimeout);