• Aucun résultat trouvé

Completion Routines

Dans le document Copyright © 2002 by Microsoft Corporation (Page 156-161)

Completion routines are the other method your application can use to manage completed overlapped I/O requests. Completion routines are simply functions that you optionally pass to an overlapped I/O request and that the system invokes when an overlapped I/O request completes. Their primary role is to service a completed I/O request using the caller's thread. In addition, applications can continue overlapped I/O processing through the completion routine.

To use completion routines for overlapped I/O requests, your application must specify a completion routine, along with a WSAOVERLAPPED structure, to an I/O bound Winsock function (described previously). A completion routine must have the following function prototype:

void CALLBACK CompletionROUTINE(

DWORD dwError, DWORD cbTransferred,

LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags

);

When an overlapped I/O request completes using a completion routine, the parameters contain the following information:

The parameter dwError specifies the completion status for the overlapped operation as indicated by lpOverlapped.

The cbTransferred parameter specifies the number of bytes that were transferred during the overlapped operation.

The lpOverlapped parameter is the same as the WSAOVERLAPPED structure passed into the originating I/O call.

The dwFlags parameter returns any flags that the operation may have completed with (such as from WSARecv).

There is a major difference between overlapped requests submitted with a completion routine and overlapped requests submitted with an event object. The WSAOVERLAPPED structure's event field, hEvent, is not used, which means you cannot associate an event object with the overlapped request.

Once you make an overlapped I/O call with a completion routine, your calling thread must eventually service the completion routine once it has completed. This requires you to place your calling thread in an alertable wait state and process the completion routine later, after the I/O operation has

completed. The WSAWaitForMultipleEvents function can be used to put your thread in an alertable wait state. The catch is that you must also have at least one event object available for the

WSAWaitForMultipleEvents function. If your application handles only overlapped requests with completion routines, you are not likely to have any event objects around for processing. As an alternative, your application can use the Windows SleepEx function to set your thread in an alertable wait state. Of course, you can also create a dummy event object that is not associated with anything. If your calling thread is always busy and not in an alertable wait state, no posted completion routine will

ever get called.

As you saw earlier, WSAWaitForMultipleEvents normally waits for event objects associated with WSAOVERLAPPED structures. This function is also designed to place your thread in an alertable wait state and to process completion routines for completed overlapped I/O requests if you set the

parameter fAlertable to TRUE. When overlapped I/O requests complete with a completion routine, the return value is WSA_IO_COMPLETION instead of an event object index in the event array. The SleepEx function provides the same behavior as WSAWaitForMultipleEvents except that it does not need any event objects. The SleepEx function is defined as

DWORD SleepEx(

DWORD dwMilliseconds, BOOL bAlertable

);

The dwMilliseconds parameter defines how long in milliseconds SleepEx will wait. If dwMilliseconds is set to INFINITE, SleepEx waits indefinitely. The bAlertable parameter determines how a completion routine will execute. If bAlertable is set to FALSE and an I/O completion callback occurs, the I/O completion function is not executed and the function does not return until the wait period specified in dwMilliseconds has elapsed. If it is set to TRUE, the completion routine executes and the SleepEx function returns WAIT_IO_COMPLETION.

The following code outlines how to structure a simple server application that is capable of managing one socket request using completion routines as described earlier.

#define DATA_BUFSIZE 4096 SOCKET AcceptSocket,

ListenSocket;

WSABUF DataBuf;

WSAEVENT EventArray[MAXIMUM_WAIT_OBJECTS];

DWORD Flags, RecvBytes, Index;

char buffer[DATA_BUFSIZE];

void main(void) {

WSAOVERLAPPED Overlapped;

// Step 1:

// Start Winsock, and set up a listening socket ...

// Step 2:

// Accept a new connection

AcceptSocket = accept(ListenSocket, NULL, NULL);

// Step 3:

ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

DataBuf.len = DATA_BUFSIZE;

DataBuf.buf = buffer;

// Step 4:

// Post an asynchronous WSARecv() request

// on the socket by specifying the WSAOVERLAPPED // structure as a parameter, and supply

// the WorkerRoutine function below as the // completion routine

if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &Overlapped, WorkerRoutine)

// Because the WSAWaitForMultipleEvents() API // requires waiting on one or more event objects,

Index = WSAWaitForMultipleEvents(1, EventArray, FALSE, WSA_INFINITE, TRUE);

// Step 6:

if (Index == WAIT_IO_COMPLETION)

{

DWORD SendBytes, RecvBytes;

DWORD Flags;

// At this point, an overlapped WSARecv() request // completed successfully. Now we can retrieve the // received data that is contained in the variable // DataBuf. After processing the received data, we // need to post another overlapped WSARecv() or // WSASend() request. For simplicity, we will post // another WSARecv() request.

Flags = 0;

ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

DataBuf.len = DATA_BUFSIZE;

DataBuf.buf = buffer;

if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes,

&Flags, &Overlapped, WorkerRoutine)

The application illustrates the following programming steps:

Create a socket and begin listening for a connection on a specified port.

1.

Accept an inbound connection.

2.

Create a WSAOVERLAPPED structure for the accepted socket.

3.

Post an asynchronous WSARecv request on the socket by specifying the WSAOVERLAPPED structure as a parameter and supplying a completion routine.

4.

Call WSAWaitForMultipleEvents with the fAlertable parameter set to TRUE and wait for an overlapped request to complete. When an overlapped request completes, the completion routine automatically executes and WSAWaitForMultipleEvents returns

WSA_IO_COMPLETION. Inside the completion routine, then post another overlapped WSARecv request with a completion routine.

5.

Verify that WSAWaitForMultipleEvents returns WSA_IO_COMPLETION.

6.

Repeat steps 5 and 6.

7.

The overlapped model provides high-performance socket I/O. It is different from all the previous models because an application posts buffers to send and receive data that the system uses directly.

That is, if an application posts an overlapped receive with a 10 KB buffer and data arrives on the socket, it is copied directly into this posted buffer. In the previous models, data would arrive and be copied to the per-socket receive buffers at which point the application is notified of the capability to read. After the application calls a receive function, the data is copied from the per-socket buffer to the application's buffer. Chapter 6 will discuss strategies for developing high-performance, scalable Winsock applications. Chapter 6 will also discuss the WSARecvMsg, AcceptEx, ConnectEx, TransmitFile, TransmitPackets, and DisconnectEx API functions in more detail.

The disadvantage of using overlapped I/O with events is, again, the limitation of being able to wait on a maximum of 64 events at a time. Completion routines are a good alternative but care must be taken to ensure that the thread that posted the operation goes into an alertable wait state in order for the completion routine to complete. Also, care should be taken to make sure that the completion routines do not perform excessive computations so that these routines may fire as fast as possible under a heavy load.

Dans le document Copyright © 2002 by Microsoft Corporation (Page 156-161)