2011年4月30日 星期六

Preface :Essentially, six types of socket I/O models are available that allow Winsock applications to manage I/O:blocking,select,WSAAsyncSelect,WSAEventSelect,overlapped, andcompletion port. This section explains the features of each I/O model and outlines how to use it to develop an application that can manage one or more socket requests. On the companion CD, you will find sample applications for each I/O model demonstrating how to develop a simple TCP echo server using the principles described in each model.Note that technically speaking, there could be a straight non-blocking I/O model—that is, an application that places all sockets into non-blocking mode withioctlsocket. However, this soon becomes unmanageable because the application will spend most of its time cycling through socket handles and I/O operations until they succeed.The blocking Model :Most Winsock programmers begin with the blocking model because it is the easiest and most straightforward model. The Winsock samples inChapter 1use this model. As we have mentioned, applications following this model typically use one or two threads per socket connection for handling I/O. Each thread will then issue blocking operations, such assendandrecv.The advantage to the blocking model is its simplicity. For very simple applications and rapid prototyping, this model is very useful.The disadvantage is that it does not scale up to many connections as the creation of more threads consumes valuable system resources.The select Model :Theselectmodel is another I/O model widely available in Winsock. We call it theselectmodel because it centers on using theselectfunction to manage I/O. The design of this model originated on UNIX-based computers featuring Berkeley socket implementations.The select model was incorporated into Winsock 1.1 to allow applications that want to avoid blocking on socket calls the capability to manage multiple sockets in an organized manner.Because Winsock 1.1 is backward-compatible with Berkeley socket implementations, a Berkeley socket application that uses theselectfunction should technically be able to run without modification.Theselectfunction can be used to determine if there is data on a socket and if a socket can be written to. The reason for having this function is to prevent your application from blocking on an I/O bound call such assendorrecvwhen a socket is in a blocking mode and to prevent theWSAEWOULDBLOCKerror when a socket is in a non-blocking mode. Theselectfunction blocks for I/O operations until the conditions specified as parameters are met. The function prototype for select is as follows :

The first parameter,nfds, is ignored and is included only for compatibility with Berkeley socket applications. You'll notice that there are three fd_set parameters: one for checking readability (readfds), one for writeability (writefds), and one for out-of-band data (exceptfds). Essentially, the fd_set data type represents a collection of sockets. Thereadfdsset identifies sockets that meet one of the following conditions :

* Data is available for reading.
* Connection has been closed, reset, or terminated.
* If listen has been called and a connection is pending, the accept function will succeed.

Thewritefdsset identifies sockets in which one of the following is true:

* Data can be sent.
* If a non-blocking connect call is being processed, the connection has succeeded.

Finally, theexceptfdsset identifies sockets in which one of the following is true:

* If a non-blocking connect call is being processed, the connection attempt failed.
* OOB data is available for reading.

For example, when you want to test a socket for readability, you must add it to thereadfdsset and wait for theselectfunction to complete. When theselectcall completes, you have to determine if your socket is still part of thereadfdsset. If so, the socket is readable—you can begin to retrieve data from it. Any two of the three parameters (readfds, writefds, exceptfds) can be null values (at least one must not be null), and any non-null set must contain at least one socket handle; otherwise, theselectfunction won't have anything to wait for. The final parameter,timeout, is a pointer to atimevalstructure that determines how long the select function will wait for I/O to complete. Iftimeoutis a null pointer,selectwill block indefinitely until at least one descriptor meets the specified criteria. Thetimevalstructure is defined as :

Thetv_secfield indicates how long to wait in seconds; thetv_usecfield indicates how long to wait in milliseconds. Thetimeoutvalue {0, 0} indicates select will return immediately, allowing an application to poll on theselectoperation. This should be avoided for performance reasons. Whenselectcompletes successfully, it returns the total number of socket handles that have I/O operations pending in thefd_setstructures. If thetimevallimit expires, it returns 0. Ifselectfails for any reason, it returnsSOCKET_ERROR.Before you can begin to use select to monitor sockets, your application has to set up either one or all of the read, write, and exceptionfd_setstructures by assigning socket handles to a set. When you assign a socket to one of the sets, you are askingselectto let you know if the I/O activities just described have occurred on a socket. Winsock provides the following set of macros to manipulate and check the fd_set sets for I/O activity :

* FD_ZERO(*set) Initializes set to the empty set. A set should always be cleared before using.* FD_CLR(s, *set) Removes socket s from set.* FD_ISSET(s, *set) Checks to see if s is a member of set and returns TRUE if so.* FD_SET(s, *set) Adds socket s to set.

For example, if you want to find out when it is safe to read data from a socket without blocking, simply assign your socket to thefd_readset using the FD_SET macro and then callselect. To test whether your socket is still part of thefd_readset, use the FD_ISSET macro. The following five steps describe the basic flow of an application that usesselectwith one or more socket handles :

1. Initialize each fd_set of interest by using the FD_ZERO macro.

2. Assign socket handles to each of the fd_set sets of interest by using the FD_SET macro.

3. Call the select function and wait until I/O activity sets one or more of the socket handles in each fd_set set provided. When select completes, it returns the total number of socket handles that are set in all of the fd_set sets and updates each set accordingly.

3. Using the return value of select, your application can determine which application sockets have I/O pending by checking each fd_set set using the FD_ISSET macro.

4. After determining which sockets have I/O pending in each of the sets, process the I/O and go to step 1 to continue the select process.

Whenselectreturns, it modifies each of thefd_setstructures by removing the socket handles that do not have pending I/O operations. This is why you should use the FD_ISSET macro as in step 4 to determine if a particular socket is part of a set. The following code sample outlines the basic steps needed to set up the select model for a single socket. Adding more sockets to this application simply involves maintaining a list or an array of additional sockets :

The advantage of using select is the capability to multiplex connections and I/O on many sockets from a single thread. This prevents the explosion of threads associated with blocking sockets and multiple connections. The disadvantage is the maximum number of sockets that may be added to the fd_set structures. By default, the maximum is defined as FD_SETSIZE, which is defined in WINSOCK2.H as 64. To increase this limit, an application might defineFD_SETSIZEto something large. This define must appear before including WINSOCK2.H. Also, the underlying provider imposes an arbitrary maximum fd_set size, which typically is 1024 but is not guaranteed to be. Finally, for a largeFD_SETSIZE, consider the performance hit of setting 1000 sockets before calling select followed by checking whether each of those 1000 sockets is set after the call returns.The WSAAsyncSelect Model :Winsock provides a useful asynchronous I/O model that allows an application to receive Windows message–based notification of network events on a socket. This is accomplished by calling theWSAAsyncSelectfunction after creating a socket. Before we continue, however, we need to make one subtle distinction. TheWSAAsyncSelectandWSAEventSelectmodels provide asynchronous notification of the capability to read or write data. It does not provide asynchronous data transfer like the overlapped and completion port models.This model originally existed in Winsock 1.1 implementations to help application programmers cope with the cooperative multitasking message-based environment of 16-bit Windows platforms, such as Windows for Workgroups. Applications can still benefit from this model, especially if they manage window messages in a standard Windows procedure, usually referred to as awinproc. This model is also used by the Microsoft Foundation Class (MFC)CSocketobject.- Message NotificationTo use theWSAAsyncSelectmodel, your application must first create a window using theCreateWindowfunction and supply a window procedure (winproc) support function for it. You can also use a dialog box with a dialog procedure instead of a window because dialog boxesarewindows. For our purposes, we will demonstrate this model using a simple window with a supporting window procedure. Once you have set up the window infrastructure, you can begin creating sockets and turning on window message notification by calling theWSAAsyncSelectfunction, which is defined as :

Thesparameter represents the socket we are interested in. ThehWndparameter is a window handle identifying the window or the dialog box that receives a message when a network event occurs. ThewMsgparameter identifies the message to be received when a network event occurs. This message is posted to the window that is identified by thehWndwindow handle. Applications usually set this message to a value greater than the WindowsWM_USERvalue to avoid confusing a network window message with a predefined standard window message. The last parameter,lEvent, represents a bitmask that specifies a combination of network events—listed in Table 5-3—that the application is interested in. Most applications are typically interested in theFD_READ,FD_WRITE,FD_ACCEPT,FD_CONNECT, andFD_CLOSEnetwork event types. If your application is interested in more than one network event, simply set this field by performing a bitwise OR on the types and assigning them to lEvent. For example :

This allows our application to get connect, send, receive, and socket-closure network event notifications on sockets. It is impossible to register multiple events one at a time on the socket. Also note that once you turn on event notification on a socket, it remains on unless the socket is closed by a call toclosesocketor the application changes the registered network event types by callingWSAAsyncSelect(again, on the socket). Setting the lEvent parameter to 0 effectively stops all network event notification on the socket.When your application calls WSAAsyncSelect on a socket, the socket mode is automatically changed from blocking to the non-blocking mode that we described previously.As a result, if a Winsock I/O call such as WSARecv is called and has to wait for data, it will fail with errorWSAEWOULDBLOCK. To avoid this error, applications should rely on the user-defined window message specified in thewMsgparameter ofWSAAsyncSelectto indicate when network event types occur on the socket.After your application successfully callsWSAAsyncSelecton a socket, the application begins to receive network event notification as Windows messages in the window procedure associated with thehWndparameter window handle. A window procedure is normally defined as :

ThehWndparameter is a handle to the window that invoked the window procedure. TheuMsgparameter indicates which message needs to be processed. In your case, you will be looking for the message defined in theWSAAsyncSelectcall. ThewParamparameter identifies the socket on which a network event has occurred. This is important if you have more than one socket assigned to this window procedure. ThelParamparameter contains two important pieces of information—the low word oflParamspecifies the network event that has occurred, and the high word oflParamcontains any error code.When network event messages arrive at a window procedure, the application should first check thelParamhigh-word bits to determine whether a network error has occurred on the socket. There is a special macro, WSAGETSELECTERROR, that returns the value of the high-word bits error information. After the application has verified that no error occurred on the socket, the application should determine which network event type caused the Windows message to fire by reading the low-word bits oflParam. Another special macro, WSAGETSELECTEVENT, returns the value of the low-word portion oflParam.The following example demonstrates how to manage window messages when using the WSAAsyncSelect I/O model. The code highlights the steps needed to develop a basic server application and removes the programming details of developing a fully featured Windows application :

One final detail worth noting is how applications should process FD_WRITE event notifications. FD_WRITE notifications are sent under only three conditions :

* After a socket is first connected with connect or WSAConnect
* After a socket is accepted with accept or WSAAccept
* When a send, WSASend, sendto, or WSASendTo operation fails with WSAEWOULDBLOCK and buffer space becomes available

Therefore, an application should assume that sends are always possible on a socket starting from the first FD_WRITE message and lasting until asend,WSASend,sendto, or WSASendToreturns the socket errorWSAEWOULDBLOCK. After such failure, another FD_WRITE message notifies the application that sends are once again possible.TheWSAAsyncSelectmodel offers many advantages; foremost is the capability to handle many connections simultaneously without much overhead, unlike the select model's requirement of setting up thefd_setstructures. The disadvantages are having to use a window if your application requires no windows (such as a service or console application). Also, having a single window procedure to service all the events on thousands of socket handles can become a performance bottleneck (meaning this model doesn't scale very well).補充說明 :*[ NP in MS ] Part2 : Socket I/O Models 之二 - WSAEventSelect Model*[ NP in MS ] Part2 : Socket I/O Models 之三 - Overlapped Model