一个命名管道是一个具名的,单工或双工通讯的管道,它可以在一个管道服务器和多个管道管道客户端之间进行通讯。所有的命名管道实例共用一个管道名称,但是每个管道实例拥有自己的缓冲区和句柄,以提供单独的服务器/客户端通讯信道。
所有的进程都可以访问命名管道,这也使得命名管道成为进程间通讯的利器。
同样,任何进程既可以充当管道服务器,也可以充当管道客户端,这使得进程间点对点通讯成为可能。
命名管道既可以用来提供同一台机器上进程间通讯的手段,也可以用来在不同机器上的不同进程间通过网络进行通讯。只要管道服务器运行起来了,所有的管道客户端都可以进行远程访问。
If you intend to use a named pipe locally only, deny access to NT AUTHORITY\NETWORK or switch to local RPC.
Pipe Names
每个命名管道都有一个唯一的名字能够和电脑上其他命名管道区分开来。 命名管道名字在管道服务器调用CreateNamedPipe创建命名管道的时候指定。管道客户端在调用CreateFile或CallNamedPipe函数的时候使用管道名字来指定连接到那一个命名管道。
在调用CreateFile, WaitNamedPipe, 和 CallNamedPipe 函数时候需要按照以下格式指定命名管道名称:
\\ServerName\pipe\PipeName
在这里ServerName可以是远程计算机名或者用一个点号来表示本地计算机。PipeName可以使用除了反斜线以为的任何字符,包括数字和特殊符号等等。 PipeName的长度不能超过256个字符,并且PipeName是不区分大小写的。
Windows Me/98/95: pipeName不能包含冒号。
管道服务器可以创建一个位于其他计算机上的命名管道,因此需要使用点号来指定本地计算机:
\\.\pipe\PipeName
Named Pipe Open Modes
管道服务器在调用CreateNamedPipe 函数的时候通过dwOpenMode 参数指定管道的access, overlap, 和write-through 模式。客户端可以 CreateFile 函数中进行指定。
Access Mode
服务器端指定的access mode和客户端指定的access mode要相互协调:
Access mode(server) | CreateFile equivalent |
---|---|
PIPE_ACCESS_INBOUND | GENERIC_READ (server only reads and client only writes) |
PIPE_ACCESS_OUTBOUND | GENERIC_WRITE (server only writes and client only reads) |
PIPE_ACCESS_DUPLEX | GENERIC_READ | GENERIC_WRITE (server and client both read and write) |
Overlapped Mode
在 overlapped mode下, 那些需要长时间读、写和连接的函数可以立即返回。(具体的可以参考Overlapped IO的内容。)
CreateFile 函数允许客户端通过指定dwFlagsAndAttributes 参数为FILE_FLAG_OVERLAPPED来使用Overlapped Mode.
Write-Through Mode(直写,相对于write-cache)
write-through mode 通过FILE_FLAG_WRITE_THROUGH来制定。 这个模式只影响位于不同计算机的服务器和客户端之间的写操作。 在write-through mode下, 写操作在把所有数据传送到远程计算机管道的缓冲区之前是不会返回的(不撞南墙不回头不回头啊......好同志啊)。 这个模式对于每个写操作都需要同步的时候特别有用。
If write-through mode is not enabled, the system enhances the efficiency of network operations by buffering data until a minimum number of bytes have accumulated or until a maximum time period has elapsed. Buffering enables the system to combine multiple write operations into a single network transmission. This means that a write operation can be successfully completed after the system puts the data in the outbound buffer, but before the system transmits it across the network.
The CreateFile function allows the pipe client to set write-through mode (FILE_FLAG_WRITE_THROUGH) for its pipe handles using the dwFlagsAndAttributes parameter. The write-through mode of a pipe handle cannot be changed after the pipe handle has been created. The write-through mode can be different for server and client handles to the same pipe instance.
A pipe client can use the SetNamedPipeHandleState function to control the number of bytes and the time-out period before transmission for a pipe on which write-through mode is disabled.
唉,翻译真无聊啊。。。。剩下的就不翻译了,大学四级过了肯定能看懂的(^_^)
Named Pipe Type, Read, and Wait Modes
The pipe server specifies the pipe type, read, and wait modes in the dwPipeMode parameter of the CreateNamedPipe function. Pipe clients can specify these pipe modes for their pipe handles using the CreateFile function.
Type Mode
The type mode of a pipe determines how data is written to a named pipe. Data can be transmitted through a named pipe as either a stream of bytes or as a stream of messages. The pipe server specifies the pipe type when calling CreateNamedPipe to create an instance of a named pipe. The type modes must be the same for all instances of a pipe.
To create a byte-type pipe, specify PIPE_TYPE_BYTE or use the default value. The data is written to the pipe as a stream of bytes, and the system does not differentiate between the bytes written in different write operations.
To create a message-type pipe, specify PIPE_TYPE_MESSAGE. The system treats the bytes written in each write operation to the pipe as a message unit. The system always performs write operations on message-type pipes as if write-through mode were enabled.
Read Mode
The read mode of a pipe determines how data is read from a named pipe. The pipe server specifies the initial read mode for a pipe handle when calling CreateNamedPipe. Data can be read in byte-read mode or message-read mode. A handle to a byte-type pipe can be in byte-read mode only. A handle to a message-type pipe can be in either byte-read or message-read mode. For a message-type pipe, the read mode can be different for server and client handles to the same pipe instance.
To create the pipe handle in byte-read mode, specify PIPE_READMODE_BYTE or use the default value. Data is read from the pipe as a stream of bytes. A read operation is completed successfully when all available bytes in the pipe are read or when the specified number of bytes is read.
To create the pipe handle in message-read mode, specify PIPE_READMODE_MESSAGE. Data is read from the pipe as a stream of messages. A read operation is completed successfully only when the entire message is read. If the specified number of bytes to read is less than the size of the next message, the function reads as much of the message as possible before returning zero (the GetLastError function returns ERROR_MORE_DATA). The remainder of the message can be read using another read operation.
For a pipe client, a pipe handle returned by CreateFile is always in byte-read mode initially. Both pipe clients and pipe servers can use the SetNamedPipeHandleState function to change the read mode of a pipe handle.
Wait Mode
The wait mode of a pipe handle determines how the ReadFile, WriteFile, and ConnectNamedPipe functions handle lengthy operations. In blocking-wait mode, the functions wait indefinitely for a process on the other end of the pipe to complete an operation. In nonblocking-wait mode, the functions return immediately in situations that would otherwise require an indefinite wait.
A ReadFile operation is affected by the wait mode of a pipe handle when the pipe is empty. With a blocking-wait handle, the operation is not completed successfully until data is available from a thread writing to the other end of the pipe. Using a nonblocking-wait handle, the function returns zero immediately, and the GetLastError function returns ERROR_NO_DATA.
A WriteFile operation is affected by the wait mode of a pipe handle when there is insufficient space in the pipe's buffer. With a blocking-wait handle, the write operation cannot succeed until sufficient space is created in the buffer by a thread reading from the other end of the pipe. With a nonblocking-wait handle, the write operation returns a nonzero value immediately, without writing any bytes (for a message-type pipe) or after writing as many bytes as the buffer holds (for a byte-type pipe).
A ConnectNamedPipe operation is affected by the wait mode of a pipe handle when there is no client connected or waiting to connect to the pipe instance. With a blocking-wait handle, the connect operation does not succeed until a pipe client connects to the pipe instance by calling either the CreateFile or CallNamedPipe function. With a nonblocking-wait handle, the connect operation returns zero immediately, and the GetLastError function returns ERROR_PIPE_LISTENING.
By default, all named pipe handles returned by the CreateNamedPipe or CreateFile function are created with blocking-wait mode enabled. To create the pipe in nonblocking-wait mode, the pipe server specifies PIPE_NOWAIT when calling CreateNamedPipe.
Both pipe clients and pipe servers can change a pipe handle's wait mode by specifying either PIPE_WAIT or PIPE_NOWAIT in a call to the SetNamedPipeHandleState function.
Note The nonblocking-wait mode is supported for compatibility with Microsoft® LAN Manager version 2.0. This mode should not be used to achieve overlapped input and output (I/O) with named pipes. Overlapped I/O should be used instead, because it enables time-consuming operations to run in the background after the function returns. For more information about overlapped I/O, see Synchronous and Overlapped Input and Output.
Named Pipe Instances
The simplest pipe server creates a single instance of a pipe, connects to a single client, communicates with the client, disconnects from the client, closes the pipe handle, and terminates. However, it is more common for a pipe server to communicate with multiple pipe clients. A pipe server could use a single pipe instance to connect with multiple pipe clients by connecting to and disconnecting from each client in sequence, but performance would be poor. The pipe server must create multiple pipe instances to efficiently handle multiple clients simultaneously.
There are three basic strategies for servicing multiple pipe instances.
- Create a separate thread for each instance of the pipe. For an example of a multithreaded pipe server, see Multithreaded Pipe Server.
- Use overlapped operations by specifying an OVERLAPPED structure in the ReadFile, WriteFile, and ConnectNamedPipe functions. For an example, see Named Pipe Server Using Overlapped I/O.
- Use overlapped operations by using the ReadFileEx and WriteFileEx functions, which specify a completion routine to be executed when the operation is completed. For an example, see Named Pipe Server Using Completion Routines.
The multithreaded pipe server is easiest to write, because the thread for each instance handles communications for a single pipe client. The system allocates processor time to each thread as needed. But each thread uses system resources, which is a disadvantage for a pipe server that handles a large number of clients.
With a single-threaded server, it is easier to coordinate operations that affect multiple clients, and it is easier to protect shared resources from simultaneous access by multiple clients. The challenge of a single-threaded server is that it requires coordination of overlapped operations to allocate processor time for handling the simultaneous needs of clients.
Named Pipe Operations
The first time the pipe server calls the CreateNamedPipe function, it uses the nMaxInstances parameter to specify the maximum number of instances of the pipe that can exist simultaneously. The server can call CreateNamedPipe repeatedly to create additional instances of the pipe, as long as it does not exceed the maximum number of instances. If the function succeeds, each call returns a handle to the server end of a named pipe instance.
As soon as the pipe server creates a pipe instance, a pipe client can connect to it by calling the CreateFile or CallNamedPipe function. If a pipe instance is available, CreateFile returns a handle to the client end of the pipe instance. If no instances of the pipe are available, a pipe client can use the WaitNamedPipe function to wait until a pipe becomes available.
A pipe server can determine when a pipe client is connected to a pipe instance by calling the ConnectNamedPipe function. If the pipe handle is in blocking-wait mode, ConnectNamedPipe does not return until a client is connected.
Pipe clients and servers can call one of several functions — in addition to CallNamedPipe— to read from and write to a named pipe. The behavior of these functions depends on the type of pipe and the modes in effect for the specified pipe handle, as follows:
- The ReadFile and WriteFile functions can be used with either byte-type or message-type pipes.
- The ReadFileEx and WriteFileEx functions can be used with either byte-type or message-type pipes if the pipe handle was opened for overlapped operations.
- The PeekNamedPipe function can be used to read without removing the contents of either a byte-type pipe or a message-type pipe. PeekNamedPipe can also return additional information about the pipe instance.
- The TransactNamedPipe function can be used with message-type duplex pipes if the pipe handle to the calling process is set to message-read mode. The function writes a request message and reads a reply message in a single operation, enhancing network performance.
The pipe server should not perform a blocking read operation until the pipe client has started. Otherwise, a race condition can occur. This typically occurs when initialization code, such as that of the C run-time library, needs to lock and examine inherited handles.
When a client and server finish using a pipe instance, the server should first call the FlushFileBuffers function, to ensure that all bytes or messages written to the pipe are read by the client. FlushFileBuffers does not return until the client has read all data from the pipe. The server then calls the DisconnectNamedPipe function to close the connection to the pipe client. This function makes the client's handle invalid, if it has not already been closed. Any unread data in the pipe is discarded. After the client is disconnected, the server calls the CloseHandle function to close its handle to the pipe instance. Alternatively, the server can use ConnectNamedPipe to enable a new client to connect to this instance of the pipe.
A process can retrieve information about a named pipe by calling the GetNamedPipeInfo function, which returns the type of the pipe, the size of the input and output buffers, and the maximum number of pipe instances that can be created. The GetNamedPipeHandleState function reports on the read and wait modes of a pipe handle, the current number of pipe instances, and additional information for pipes that communicate over a network. The SetNamedPipeHandleState function sets the read mode and wait modes of a pipe handle. For pipe clients communicating with a remote server, the function also controls the maximum number of bytes to collect or the maximum time to wait before transmitting a message (assuming the client's handle was not opened with write-through mode enabled).
Synchronous and Overlapped Pipe I/O
The ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe functions can perform input and output operations on a pipe either synchronously or asynchronously. When a function runs synchronously, it does not return until the operation it is performing is completed. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to be completed. When a function runs asynchronously, it returns immediately, even if the operation has not been completed. This enables a time-consuming operation to be executed in the background while the calling thread is free to perform other tasks.
Using asynchronous I/O enables a pipe server to use a loop that performs the following steps:
- Specify multiple event objects in a call to the wait function, and wait for one of the objects to be set to the signaled state.
- Use the wait function's return value to determine which overlapped operation has finished.
- Perform the tasks necessary to clean up the completed operation and initiate the next operation for that pipe handle. This can involve starting another overlapped operation for the same pipe handle.
Overlapped operations make it possible for one pipe to read and write data simultaneously and for a single thread to perform simultaneous I/O operations on multiple pipe handles. This enables a single-threaded pipe server to handle communications with multiple pipe clients efficiently. For an example, see Named Pipe Server Using Overlapped I/O.
For a pipe server to use synchronous operations to communicate with more than one client, it must create a separate thread for each pipe client so that one or more threads can run while other threads are waiting. For an example of a multithreaded pipe server that uses synchronous operations, see Multithreaded Pipe Server.
Enabling Asynchronous Operation
The ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe functions can be performed asynchronously only if you enable overlapped mode for the specified pipe handle and specify a valid pointer to an OVERLAPPED structure. If the OVERLAPPED pointer is NULL, the function return value can incorrectly indicate that the operation has been completed. Therefore, it is strongly recommended that if you create a handle with FILE_FLAG_OVERLAPPED and want asynchronous behavior, you should always specify a valid OVERLAPPED structure.
The hEvent member of the specified OVERLAPPED structure must contain a handle to a manual-reset event object. This is a synchronization object created by the CreateEvent function. The thread that initiates the overlapped operation uses the event object to determine when the operation has finished. You should not use the pipe handle for synchronization when performing simultaneous operations on the same handle because there is no way of knowing which operation's completion caused the pipe handle to be signaled. The only reliable technique for performing simultaneous operations on the same pipe handle is to use a separate OVERLAPPED structure with its own event object for each operation. For more information about event objects, see Synchronization.
When ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe operations are performed asynchronously, one of the following occurs:
- If the operation is complete when the function returns, the return value indicates the success or failure of the operation. If an error occurs, the return value is zero and the GetLastError function returns something other than ERROR_IO_PENDING.
- If the operation has not finished when the function returns, the return value is zero and GetLastError returns ERROR_IO_PENDING. In this case, the calling thread must wait until the operation has finished. The calling thread must then call the GetOverlappedResult function to determine the results.
Using Completion Routines
The ReadFileEx and WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped ReadFile and WriteFile functions, which use an event object to signal completion, the extended functions specify a completion routine. A completion routine is a function that is queued for execution when the read or write operation is finished. The completion routine is not executed until the thread that called ReadFileEx and WriteFileEx starts an alertable wait operation by calling one of the alertable wait functions with the fAlertable parameter set to TRUE. In an alertable wait operation, the functions also return when a ReadFileEx or WriteFileEx completion routine is queued for execution. A pipe server can use the extended functions to perform a sequence of read and write operations for each client that connects to it. Each read or write operation in the sequence specifies a completion routine, and each completion routine initiates the next step in the sequence. For an example, see Named Pipe Server Using Completion Routines.
Named Pipe Security and Access Rights
Windows security enables you to control access to named pipes. For more information about security, see Access-Control Model.
You can specify a security descriptor for a named pipe when you call the CreateNamedPipe function. The security descriptor controls access to both client and server ends of the named pipe. If you specify NULL, the named pipe gets a default security descriptor. The ACLs in the default security descriptor for a named pipe grant full control to the LocalSystem account, administrators, and the creator owner. They also grant read access to members of the Everyone group and the anonymous account.
To retrieve a named pipe's security descriptor, call the GetSecurityInfo function. To change the security descriptor of a named pipe, call the SetSecurityInfo function.
When a thread calls CreateNamedPipe to open a handle to the server end of an existing named pipe, the system performs an access check before returning the handle. The access check compares the thread's access token and the requested access rights against the DACL in the named pipe's security descriptor. In addition to the requested access rights, the DACL must allow the calling thread FILE_CREATE_PIPE_INSTANCE access to the named pipe.
Similarly, when a client calls the CreateFile or CallNamedPipe function to connect to the client end of a named pipe, the system performs an access check before granting access to the client.
The handle returned by the CreateNamedPipe function always has SYNCHRONIZE access. It also has GENERIC_READ, GENERIC_WRITE, or both, depending on the open mode of the pipe. The following are the access rights for each open mode.
Open mode | Access rights |
---|---|
PIPE_ACCESS_DUPLEX (0x00000003) | FILE_GENERIC_READ, FILE_GENERIC_WRITE, and SYNCHRONIZE |
PIPE_ACCESS_INBOUND (0x00000001) | FILE_GENERIC_READ and SYNCHRONIZE |
PIPE_ACCESS_OUTBOUND (0x00000002) | FILE_GENERIC_WRITE and SYNCHRONIZE |
FILE_GENERIC_READ access for a named pipe combines the rights to read data from the pipe, read pipe attributes, read extended attributes, and read the pipe's DACL.
FILE_GENERIC_WRITE access for a named pipe combines the rights to write data to the pipe, append data to it, write pipe attributes, write extended attributes, and read the pipe's DACL. Because FILE_APPEND_DATA and FILE_CREATE_PIPE_INSTANCE have the same definition, so FILE_GENERIC_WRITE enables permission to create the pipe. To avoid this problem, use the individual rights instead of using FILE_GENERIC_WRITE.
You can request the ACCESS_SYSTEM_SECURITY access right to a named pipe object if you want to read or write the object's SACL. For more information, see Access-Control Lists (ACLs) and SACL Access Right.
To prevent remote users or users on a different terminal services session from accessing a named pipe, use the logon SID on the DACL for the pipe. The logon SID is used in run-as logons as well; it is the SID used to protect the per-session object namespace. For more information, see Getting the Logon SID in C++.
Impersonating a Named Pipe Client
Impersonation is the ability of a thread to execute in a security context different from that of the process that owns the thread. Impersonation enables the server thread to perform actions on behalf of the client, but within the limits of the client's security context. The client typically has some lesser level of access rights. For more information, see Impersonation.
A named pipe server thread can call the ImpersonateNamedPipeClient function to assume the access token of the user connected to the client end of the pipe. For example, a named pipe server can provide access to a database or file system to which the pipe server has privileged access. When a pipe client sends a request to the server, the server impersonates the client and attempts to access the protected database. The system then grants or denies the server's access, based on the security level of the client. When the server is finished, it uses the RevertToSelf function to restore its original security token.
The impersonation level determines the operations the server can perform while impersonating the client. By default, a server impersonates at the SecurityImpersonation impersonation level. However, when the client calls the CreateFile function to open a handle to the client end of the pipe, the client can use the SECURITY_SQOS_PRESENT flag to control the server's impersonation level.