Winsock provides another useful asynchronous event notification I/O model that is similar to the WSAAsyncSelect model that allows an application to receive event-based notification of network events on one or more sockets. This model is similar to the WSAAsyncSelect model because your application receives and processes the same network events listed in Table 5-3 that the WSAAsyncSelect model uses. The major difference with this model is that network events are notified via an event object handle instead of a window procedure.
- Event Notification
The event notification model requires your application to create an event object for each socket used by calling the WSACreateEvent function, which is defined as :
- WSAEVENT WSACreateEvent(void);
- int WSAEventSelect(
- SOCKET s,
- WSAEVENT hEventObject,
- long lNetworkEvents
- );
The event created for WSAEventSelect has two operating states and two operating modes. The operating states are known as signaled and non-signaled. The operating modes are known as manual reset and auto reset. WSACreateEvent initially creates event handles in a non-signaled operating state with a manual reset operating mode. As network events trigger an event object associated with a socket, the operating state changes from non-signaled to signaled. Because the event object is created in a manual reset mode, your application is responsible for changing the operating state from signaled to non-signaled after processing an I/O request. This can be accomplished by calling the WSAResetEvent function, which is defined as :
- BOOL WSAResetEvent(WSAEVENT hEvent);
- BOOL WSACloseEvent(WSAEVENT hEvent);
Once a socket is associated with an event object handle, the application can begin processing I/O by waiting for network events to trigger the operating state of the event object handle. The WSAWaitForMultipleEvents function is designed to wait on one or more event object handles and returns either when one or all of the specified handles are in the signaled state or when a specified timeout interval expires. WSAWaitForMultipleEvents is defined as :
- DWORD WSAWaitForMultipleEvents(
- DWORD cEvents,
- const WSAEVENT FAR * lphEvents,
- BOOL fWaitAll,
- DWORD dwTimeout,
- BOOL fAlertable
- );
Note that by servicing signaled events one at a time (by setting the fWaitAll parameter to FALSE), it is possible to starve sockets toward the end of the event array. Consider the following code:
- WSAEVENT HandleArray[WSA_MAXIMUM_WAIT_EVENTS];
- int WaitCount=0, ret, index;
- // Assign event handles into HandleArray
- while (1) {
- ret = WSAWaitForMultipleEvents(
- WaitCount,
- HandleArray,
- FALSE,
- WSA_INFINITE,
- TRUE);
- if ((ret != WSA_WAIT_FAILED) && (ret != WSA_WAIT_TIMEOUT)) {
- index = ret - WSA_WAIT_OBJECT_0;
- // Service event signaled on HandleArray[index]
- WSAResetEvent(HandleArray[index]);
- }
- }
When WSAWaitForMultipleEvents receives network event notification of an event object, it returns a value indicating the event object that caused the function to return. As a result, your application can determine which network event type is available on a particular socket by referencing the signaled event in the event array and matching it with the socket associated with the event. When you reference the events in the event array, you should reference them using the return value of WSAWaitForMultipleEvents minus the predefined value WSA_WAIT_EVENT_0. For example :
- Index = WSAWaitForMultipleEvents(...);
- MyEvent = EventArray[Index - WSA_WAIT_EVENT_0];
- int WSAEnumNetworkEvents(
- SOCKET s,
- WSAEVENT hEventObject,
- LPWSANETWORKEVENTS lpNetworkEvents
- );
- typedef struct _WSANETWORKEVENTS
- {
- long lNetworkEvents;
- int iErrorCode[FD_MAX_EVENTS];
- } WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
Ps.
The iErrorCode parameter is an array of error codes associated with the events in lNetworkEvents. For each network event type, there is a special event index similar to the event type names—except for an additional “_BIT” string appended to the event name. For example, for the FD_READ event type, the index identifier for the iErrorCode array is named FD_READ_BIT. The following code fragment demonstrates this for an FD_READ event :
- // Process FD_READ notification
- if (NetworkEvents.lNetworkEvents & FD_READ)
- {
- if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
- {
- printf("FD_READ failed with error %d\n",
- NetworkEvents.iErrorCode[FD_READ_BIT]);
- }
- }
- SOCKET SocketArray [WSA_MAXIMUM_WAIT_EVENTS];
- WSAEVENT EventArray [WSA_MAXIMUM_WAIT_EVENTS],
- NewEvent;
- SOCKADDR_IN InternetAddr;
- SOCKET Accept, Listen;
- DWORD EventTotal = 0;
- DWORD Index, i;
- // Set up a TCP socket for listening on port 5150
- Listen = socket (PF_INET, SOCK_STREAM, 0);
- InternetAddr.sin_family = AF_INET;
- InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- InternetAddr.sin_port = htons(5150);
- bind(Listen, (PSOCKADDR) &InternetAddr,
- sizeof(InternetAddr));
- NewEvent = WSACreateEvent();
- WSAEventSelect(Listen, NewEvent,
- FD_ACCEPT │ FD_CLOSE);
- listen(Listen, 5);
- SocketArray[EventTotal] = Listen;
- EventArray[EventTotal] = NewEvent;
- EventTotal++;
- while(TRUE)
- {
- // Wait for network events on all sockets
- Index = WSAWaitForMultipleEvents(EventTotal,
- EventArray, FALSE, WSA_INFINITE, FALSE);
- Index = Index - WSA_WAIT_EVENT_0;
- // Iterate through all events to see if more than one is signaled
- for(i=Index; i < EventTotal ;i++
- {
- Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000,
- FALSE);
- if ((Index == WSA_WAIT_FAILED) ││ (Index == WSA_WAIT_TIMEOUT))
- continue;
- else
- {
- Index = i;
- WSAEnumNetworkEvents(
- SocketArray[Index],
- EventArray[Index],
- &NetworkEvents);
- // Check for FD_ACCEPT messages
- if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
- {
- if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
- {
- printf("FD_ACCEPT failed with error %d\n",
- NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
- break;
- }
- // Accept a new connection, and add it to the
- // socket and event lists
- Accept = accept(
- SocketArray[Index],
- NULL, NULL);
- // We cannot process more than
- // WSA_MAXIMUM_WAIT_EVENTS sockets, so close
- // the accepted socket
- if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
- {
- printf("Too many connections");
- closesocket(Accept);
- break;
- }
- NewEvent = WSACreateEvent();
- WSAEventSelect(Accept, NewEvent,
- FD_READ │ FD_WRITE │ FD_CLOSE);
- EventArray[EventTotal] = NewEvent;
- SocketArray[EventTotal] = Accept;
- EventTotal++;
- printf("Socket %d connected\n", Accept);
- }
- // Process FD_READ notification
- if (NetworkEvents.lNetworkEvents & FD_READ)
- {
- if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
- {
- printf("FD_READ failed with error %d\n",
- NetworkEvents.iErrorCode[FD_READ_BIT]);
- break;
- }
- // Read data from the socket
- recv(SocketArray[Index - WSA_WAIT_EVENT_0],
- buffer, sizeof(buffer), 0);
- }
- // Process FD_WRITE notification
- if (NetworkEvents.lNetworkEvents & FD_WRITE)
- {
- if (NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0)
- {
- printf("FD_WRITE failed with error %d\n",
- NetworkEvents.iErrorCode[FD_WRITE_BIT]);
- break;
- }
- send(SocketArray[Index - WSA_WAIT_EVENT_0],
- buffer, sizeof(buffer), 0);
- }
- if (NetworkEvents.lNetworkEvents & FD_CLOSE)
- {
- if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0)
- {
- printf("FD_CLOSE failed with error %d\n",
- NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
- break;
- }
- closesocket(SocketArray[Index]);
- // Remove socket and associated event from
- // the Socket and Event arrays and decrement
- // EventTotal
- CompressArrays(EventArray, SocketArray, &EventTotal);
- }
- }
- }
- }
沒有留言:
張貼留言