In front of us, we talked about the WSAAsyncSelect model, which provides a mechanism compared to the Select model: when the corresponding IO notification occurs, the operating system is notified immediately, and the corresponding handler function is called, which solves the timing problem of calling send and recv. But it has one obvious drawback, that it has to rely on Windows. Another model is provided for this Winsock WSAEventSelect
Introduction to the Model
The main feature of the model is that it uses event handlers to complete notification of socket events. Similar to the WSAAsyncSelect model, it also allows you to use event objects to complete notifications for multiple sockets.
The model first calls Wsacreateevent on each socket handle to create a Wsaevent object handle (the earlier wsaevent differs from the traditional event handle, but it is the same thing from the WinSock2.0). Then call WSAEventSelect to bind the socket handle to the Wsaevent object, and eventually wsawaitformultievents to wait for the wsaevent to become signaled before processing the corresponding socket.
Wsaevent has two modes of operation and working status
The working state has the signal and no signal two kinds
The working mode has manual reset and manual reset, and manual reset means that Wsaevent does not automatically change to no signal when wsawaitformultievents or wsawaitforsingleevents returns. You need to call wsaresetevent manually to set the Wsaevent object to no signal, and an automatic reset means that each time the wait function returns, it is automatically reset to no signal, and the Wsaevent object created by the call wsacreateevent needs to be reset manually. If you want to create an Wsaevent object that is automatically reset, you can call the CreateEvent function to create it (because there is no difference between the two after WinSock2.0, you only need to call CreateEvent and convert the return value to Wsaevent)
The WSAEventSelect function is prototyped as follows:
int WSAEventSelect( SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
where s means that the corresponding socket,heventobject represents the corresponding Wsaevent object, lnetworkevents indicates which events we need to handle, and it has some corresponding macro definitions
Network Events |
the corresponding meaning |
Fd_read |
Data receive operations are currently available, and functions such as recv, Recvfrom, WSARecv, or wsarecvfrom can be called at this time |
Fd_write |
You can send data at this time, you can call send, SendTo, WSASend, or WSASendTo |
Fd_accept |
You can call accept (Windows Sockets) or wsaaccept unless the error code returned is Wsatry_again. |
Fd_connect |
Indicates that a remote server can currently be connected |
Fd_close |
Currently receiving a shutdown message |
When WSAWaitForMultipleEvents returns, it also returns an ordinal that identifies which wsaevent in the array has a signal, and we use it to get the subscript for the index - WSA_WAIT_EVENT_0
corresponding wsaevent in the array. Then the corresponding socket can be found based on the event object.
After obtaining the corresponding socket, we also need to obtain the current event which causes it to become signaled, we can call the Wsaenumnetworkevents function to get the corresponding network event.
int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
S is the socket handle to get its specific event notification
Heventobject is the corresponding wsaevent handle, can not be passed in, because the socket handle has been explained to get the notification on that handle, of course, if passed in, then this function will do a reset to this wsaevent, set as no signal state, Equivalent to a wsaresetevent call. At this point, we don't need to call the Wsaresetevent function.
The last parameter is a struct, and the structure is defined as follows:
typedef struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS];} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
The first data is a network event that is currently generated.
The Ierrorcode array is the error code that corresponds to each network event, for each event error code its specific array subscript is a predefined set of Fd_ that begins with a string followed by a _bit ending macro, such as Fd_read event corresponding error code subscript is Fd_read_bit
The following code shows an example code that handles event errors that receive (read) data
if (NetworkEvents.lNetworkEvents & FD_READ){ if0) { printf("FD_READ failed with error %d\n", NetworkEvents.iErrorCode[FD_READ_BIT]); }}
So far, we can summarize the steps to use the WSAEventSelect model
- Call Wsacreateevent to create a wait object for each socket and map it to the corresponding socket
- Call the WSAEventSelect function to bind the socket to the Wsaevent object
- Call the WSAWaitForMultipleEvents function to wait for all socket handles
- When the WSAWaitForMultipleEvents function returns, the corresponding Wsaevent object and the socket object are found using the returned index
- Call wsaenumnetworkevents to get the corresponding network events, according to network events to do the corresponding send and receive operations
- Steps to repeat
Example
Here's a simple example.
int _tmain ( int argc, TCHAR *argv[]) {wsadata WD = {0 }; WSAStartup (Makeword (2 , 2 ), &WD); Socket skserver = socket (af_inet, sock_stream, IPPROTO_IP); Sockaddr_in addrserver = {af_inet}; Addrserver.sin_port = htons (Server_port); ADDRSERVER.SIN_ADDR.S_ADDR = htonl (Inaddr_any); Bind (Skserver, (sockaddr*) &addrserver, sizeof (sockaddr)); Listen (Skserver, 5 ); printf (" service is listening ..... \n ); Cwsaevent wsaevent; Wsaevent.insertclient (Skserver, fd_accept | Fd_close); Wsaevent.eventloop (); WSACleanup (); return 0 ;}
A class cwsaevent is defined in the code that encapsulates the operations associated with the model and the corresponding event object and socket object, first creating a listening socket in the main function, then binding, listening, and submitting the listener socket to the class to manage it. The function insertclient is defined as follows:
voidlong lNetworkEvents){ m_socketArray[m_nTotalItem] = skClient; m_EventArray[m_nTotalItem] = WSACreateEvent(); WSAEventSelect(skClient, m_EventArray[m_nTotalItem++], lNetworkEvents);}
This function adds the corresponding member to the corresponding location of the event array and the socket array, and then calls WSAEventSelect.
The EventLoop function of the class defines a loop to repeat the preceding steps, and some of the code for the function is as follows:
intCwsaevent::waitforallclient () {DWORD dwret = wsawaitformultipleevents (M_ntotalitem, M_eventarray, FALSE, WSA_INFINITE , FALSE); Wsaresetevent (M_eventarray[dwret-wsa_wait_event_0]);returnDwret-wsa_wait_event_0;}intCwsaevent::eventloop () {wsanetworkevents Wne = {0}; while(TRUE) {intNret = Waitforallclient (); Wsaenumnetworkevents (M_socketarray[nret], M_eventarray[nret], &wne);if(Wne.lnetworkevents & Fd_accept) {if(0! = Wne.ierrorcode[fd_accept_bit]) {onaccepterror (Nret, M_socketarray[nret], wne.ierrorcode[fd_a Ccept_bit]); }Else{onacccept (nret, M_socketarray[nret]); } }Else if(Wne.lnetworkevents & Fd_close) {if(0! = Wne.ierrorcode[fd_close_bit]) {oncloseerror (Nret, M_socketarray[nret], Wne.ierrorcode[fd_clo Se_bit]); }Else{OnClose (nret, M_socketarray[nret]); } }Else if(Wne.lnetworkevents & Fd_read) {if(0! = Wne.ierrorcode[fd_read_bit]) {onreaderror (Nret, M_socketarray[nret], Wne.ierrorcode[fd_read_ BIT]); }Else{OnRead (nret, M_socketarray[nret]); } }Else if(Wne.lnetworkevents & Fd_write) {if(0! = Wne.ierrorcode[fd_write_bit]) {onwriteerror (Nret, M_socketarray[nret], Wne.ierrorcode[fd_wri Te_bit]); }Else{Onwrite (nret, M_socketarray[nret]); } } }}
The function first waits, when the wait function returns, obtains the corresponding subscript, obtains to the socket and the event object, then calls the wsaenumnetworkevents to obtain the corresponding network event, finally according to the event calls the different processing function to handle
In the above code, this loop has a potential problem, we envision a scenario where there are multiple clients connecting to the server at the same time, the first time we wait to return, we focus on the processing of the IO event, that is, responding to this client A's request, when client A sends a request, And a few other client B then also sent a request, after the first processing is completed, wait for the client A, and the subsequent client B's request is queued up, if the client A has been sending requests continuously, the problem is that the server has been responding to a request, and for B, Its request was slow to respond. To avoid this problem, we can set the number of waits to 1 for each socket loop in the array after the function wsawaitformultipleevents returns, and set the timeout value to 0, At this point, the function is equivalent to looking at each socket in the array to see if it is pending, when all the traversal is complete, processing the requests in turn, or specifically creating the corresponding thread to handle the request.
Finally, the entire sample code
WinSock WSAEventSelect Model