使用完成常式的具名管道伺服器
下列範例是單一執行緒管道伺服器,可建立訊息類型管道並使用重迭的作業。 它會使用擴充函式 ReadFileEx 和 WriteFileEx ,使用完成常式執行重迭的 I/O,此常式會在作業完成時排入佇列以供執行。 管道伺服器會使用 WaitForSingleObjectEx 函式,此函式會執行可警示的等候作業,此作業會在完成常式準備好執行時傳回。 當事件物件收到訊號時,等候函式也會傳回,在此範例中,表示重迭 的 ConnectNamedPipe 作業已完成, (新的用戶端已連線) 。 此管道伺服器可以搭配 具名管道用戶端中所述的管道用戶端使用。
一開始,管道伺服器會建立管道的單一實例,並啟動重迭的 ConnectNamedPipe 作業。 當用戶端連線時,伺服器會配置結構來提供該管道實例的儲存體,然後呼叫 ReadFileEx 函式來啟動 I/O 作業序列,以處理與用戶端的通訊。 每個作業都會指定一個完成常式,以在序列中執行下一個作業。 當用戶端中斷連線且管道實例關閉時,序列就會終止。 啟動新用戶端的作業順序之後,伺服器會建立另一個管道實例,並等候下一個用戶端連線。
ReadFileEx和WriteFileEx函式的參數會指定完成常式和重迭結構的指標。 此指標會傳遞至其 lpOverLap 參數中的完成常式。 由於 OVERLAPPED 結構指向配置給每個管道實例之結構中的第一個成員,因此完成常式可以使用其 lpOverLap 參數來存取管道實例的結構。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;
VOID DisconnectAndClose(LPPIPEINST);
BOOL CreateAndConnectInstance(LPOVERLAPPED);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);
VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
HANDLE hPipe;
int _tmain(VOID)
{
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbRet;
BOOL fSuccess, fPendingIO;
// Create one event object for the connect operation.
hConnectEvent = CreateEvent(
NULL, // default security attribute
TRUE, // manual reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hConnectEvent == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
oConnect.hEvent = hConnectEvent;
// Call a subroutine to create one instance, and wait for
// the client to connect.
fPendingIO = CreateAndConnectInstance(&oConnect);
while (1)
{
// Wait for a client to connect, or for a read or write
// operation to be completed, which causes a completion
// routine to be queued for execution.
dwWait = WaitForSingleObjectEx(
hConnectEvent, // event object to wait for
INFINITE, // waits indefinitely
TRUE); // alertable wait enabled
switch (dwWait)
{
// The wait conditions are satisfied by a completed connect
// operation.
case 0:
// If an operation is pending, get the result of the
// connect operation.
if (fPendingIO)
{
fSuccess = GetOverlappedResult(
hPipe, // pipe handle
&oConnect, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // does not wait
if (!fSuccess)
{
printf("ConnectNamedPipe (%d)\n", GetLastError());
return 0;
}
}
// Allocate storage for this instance.
lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
{
printf("GlobalAlloc failed (%d)\n", GetLastError());
return 0;
}
lpPipeInst->hPipeInst = hPipe;
// Start the read operation for this client.
// Note that this same routine is later used as a
// completion routine after a write operation.
lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst);
// Create new pipe instance for the next client.
fPendingIO = CreateAndConnectInstance(
&oConnect);
break;
// The wait is satisfied by a completed read or write
// operation. This allows the system to execute the
// completion routine.
case WAIT_IO_COMPLETION:
break;
// An error occurred in the wait function.
default:
{
printf("WaitForSingleObjectEx (%d)\n", GetLastError());
return 0;
}
}
}
return 0;
}
// CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as a completion routine after writing to
// the pipe, or when a new client has connected to a pipe instance.
// It starts another read operation.
VOID WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The write operation has finished, so read the next request (if
// there is no error).
if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chRequest,
BUFSIZE*sizeof(TCHAR),
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
// Disconnect if an error occurred.
if (! fRead)
DisconnectAndClose(lpPipeInst);
}
// CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as an I/O completion routine after reading
// a request from the client. It gets data and writes it to the pipe.
VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The read operation has finished, so write a response (if no
// error occurred).
if ((dwErr == 0) && (cbBytesRead != 0))
{
GetAnswerToRequest(lpPipeInst);
fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chReply,
lpPipeInst->cbToWrite,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}
// Disconnect if an error occurred.
if (! fWrite)
DisconnectAndClose(lpPipeInst);
}
// DisconnectAndClose(LPPIPEINST)
// This routine is called when an error occurs or the client closes
// its handle to the pipe.
VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
// Disconnect the pipe instance.
if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
// Close the handle to the pipe instance.
CloseHandle(lpPipeInst->hPipeInst);
// Release the storage for the pipe instance.
if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}
// CreateAndConnectInstance(LPOVERLAPPED)
// This function creates a pipe instance and connects to the client.
// It returns TRUE if the connect operation is pending, and FALSE if
// the connection has been completed.
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | // message read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // unlimited instances
BUFSIZE*sizeof(TCHAR), // output buffer size
BUFSIZE*sizeof(TCHAR), // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // default security attributes
if (hPipe == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
// Call a subroutine to connect to the new client.
return ConnectToNewClient(hPipe, lpoOverlap);
}
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
// Start an overlapped connection for this pipe instance.
fConnected = ConnectNamedPipe(hPipe, lpo);
// Overlapped ConnectNamedPipe should return zero.
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
// Client is already connected, so signal an event.
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
// If an error occurs during the connect operation...
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
VOID GetAnswerToRequest(LPPIPEINST pipe)
{
_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}
相關主題