WSAEventSelect Model Programming
This model is a simple asynchronous event model, which is convenient to use, now say its specific usage and need to pay attention to the place.
One, the model's routines (server side):
Let's first cite an example of Wang Yanping network communication:
//////////////////////////////////////////////////
WSAEventSelect file
#include "Initsock.h"
#include <stdio.h>
#include <iostream.h>
#include <windows.h>
Initializing the Winsock library
Cinitsock Thesock;
int main ()
{
Event handle and nested handle table
Wsaevent Eventarray[wsa_maximum_wait_events];
SOCKET Sockarray[wsa_maximum_wait_events];
int neventtotal = 0;
USHORT nport = 4567; Port number that this server listens on
Create a listener section word
Socket Slisten =:: Socket (af_inet, sock_stream, ipproto_tcp);
Sockaddr_in sin;
sin.sin_family = af_inet;
Sin.sin_port = htons (nport);
Sin.sin_addr. S_un. S_ADDR = Inaddr_any;
if (:: Bind (Slisten, (sockaddr*) &sin, sizeof (sin)) = = Socket_error)
{
printf ("Failed bind () \ n");
return-1;
}
:: Listen (Slisten, 5);
Create an Event object and associate it with a new section word
Wsaevent event =:: Wsacreateevent ();
:: WSAEventSelect (Slisten, event, fd_accept| Fd_close);
Add to Table
Eventarray[neventtotal] = event;
Sockarray[neventtotal] = Slisten;
neventtotal++;
Handling Network Events
while (TRUE)
{
Wait on all event objects
int nIndex =:: WSAWaitForMultipleEvents (Neventtotal, Eventarray, False, Wsa_infinite, false);
Call the WSAWaitForMultipleEvents function on each event to determine its state
NIndex = Nindex-wsa_wait_event_0;
for (int i=nindex; i<neventtotal; i++)
{
NIndex =:: WSAWaitForMultipleEvents (1, &eventarray[i], TRUE, $, FALSE);
if (NIndex = = Wsa_wait_failed | | nIndex = = wsa_wait_timeout)
{
Continue
}
Else
{
Gets the incoming notification message, and the Wsaenumnetworkevents function automatically resets the trusted event
Wsanetworkevents event;
:: Wsaenumnetworkevents (Sockarray[i], eventarray[i], &event);
if (event.lnetworkevents & fd_accept)//processing FD_ACCEPT notification messages
{
if (event.ierrorcode[fd_accept_bit] = = 0)
{
if (Neventtotal > Wsa_maximum_wait_events)
{
printf ("Too many connections! \ n ");
Continue
}
SOCKET snew =:: Accept (sockarray[i], NULL, NULL);
Wsaevent event =:: Wsacreateevent ();
:: WSAEventSelect (Snew, event, fd_read| fd_close| Fd_write);
Add to Table
Eventarray[neventtotal] = event;
Sockarray[neventtotal] = snew;
neventtotal++;
}
}
else if (event.lnetworkevents & Fd_read)//handling Fd_read notification messages
{
if (event.ierrorcode[fd_read_bit] = = 0)
{
Char sztext[256];
int nrecv =:: Recv (Sockarray[i], Sztext, strlen (Sztext), 0);
if (Nrecv > 0)
{
SZTEXT[NRECV] = ' + ';
printf ("Received data:%s \ n", Sztext);
}
}
}
else if (event.lnetworkevents & fd_close)//handling Fd_close notification messages
{
if (event.ierrorcode[fd_close_bit] = = 0)
{
:: Closesocket (Sockarray[i]);
for (int j=i; j<neventtotal-1; j + +)
{
SOCKARRAY[J] = sockarray[j+1];
EVENTARRAY[J] = eventarray[j+1];
}
neventtotal--;
}
}
else if (event.lnetworkevents & fd_write)//handling Fd_write notification messages
{
}
}
}
}
return 0;
}
Second, the analysis of the routine
1. Creation and binding of events
Some of the previous settings we skipped, starting with wsaevent, trace discovery has the following definition in Winsock2.h:
#define Wsaevent HANDLE
This event description is a handle, we know that there are two states in the event, one is the manual handling of the event, one is automatic, here use Wsacreateevent () This function to create the return event handle, the normal return of the case, it is created by a manual handle handle, otherwise, Its return wsa_invalid_event, indicating that the creation was unsuccessful, if need to know more information wsagetlasterror () This function to get a specific information error code. there's an ambush for a ray , why it was created by hand-handled events (manually reset), then why not Wsaresetevent () This function to handle the event, first note.
And then I went on to say,
:: WSAEventSelect (Slisten, event, fd_accept| Fd_close); //Add to Table
Eventarray[neventtotal] = event;
Sockarray[neventtotal] = Slisten;
neventtotal++;
The event is bound to the listening socket, here we only for this socket receive and close two messages are interested, so only listen to these two messages, the other read and write what, do not hurry, slowly looking down. Eventarray and Sockarray, defined as wsa_maximum_wait_events size, and a # define wsa_maximum_wait_events (maximum_wait_objects) in the header file,
The latter is defined as, it is also important to note that this model single-threaded can only handle up to 64 events, and then only multi-threaded, but here is the point to illustrate, this model even if you use multi-threading,
can handle up to 1200 or so of the amount of processing (normal),Otherwise, the overall performance of the program will be reduced, as to how to decline, there really is no real test, just from the book and the information is said.
Then originally, the program then entered the dead loop, in this loop, because it is simple to use, so a lot of exceptions are not controlled, but in order to illustrate the use, it must be simple, right?
2. Event monitoring and control processing
2.1 Monitoring of events
int nIndex =:: WSAWaitForMultipleEvents (Neventtotal, Eventarray, False, Wsa_infinite, false); NIndex = Nindex-wsa_wait_event_0;
Let's first say why this index subtracts the value of WSA_WAIT_EVENT_0 because the starting value of the event is defined in the kernel, but here the final definition of this thing is still 0. And then we look at this function
:: WSAWaitForMultipleEvents (Neventtotal, Eventarray, False, Wsa_infinite, false),
This function is used to listen to the state of multiple events (that is, the events we have bound above), to return if a state or event is triggered, or to follow the parameters you set.
The previous two parameters, the first is the number of listening, the smallest is one, MSDN, the second is an array of events, the third is a wonderful place, if set to true, then only this second event in the array of all the events are trusted or triggered, before the action, if it is false, You can move as long as you have one. The fifth is the timeout setting, which can be 0, wsa_infinite, or other values, there is a problem, if set to 0 will cause the program's CPU utilization is too high, wsa_infinite may appear in the wait quantity is a word, and the third parameter is set to True, Causes long-term blocking of dead sockets. So still set as an experience value for good, as to how much this experience is, see your program's specific application。
In fact, this functionEssentially, call the Waitformulipleobjectsex function ., MSDN on the WSAEventSelect model in waiting without CPU time, this is the reason, so it is more efficient than blocking socket communication, in fact, the model of the message wsaasycselect and the model of the event is similar, the same wonderful. However, the scope of application is different, this can be used on the wince. The message is not.
And here's another point of attention, in this model,If there are several events at the same time that are believed, or triggered by, then nindex =:: WSAWaitForMultipleEvents ()returns only one of the previous events, then how to solve its back, the book has Yue: multiple loops Call this can be, so will lead to the following again in the For loop call
NIndex =:: WSAWaitForMultipleEvents (1, &eventarray[i], TRUE, $, FALSE);
Notice here the change in the parameters, the number is 1, the event is [i], but the event will continue to grow, the full letter changed to True, the timeout is 1000, the last parameter can only be set to false, specifically why check MSDN.
If we deal with the bad here, if you change 1000 to infinite wait, you can appear above the dead socket of the infinite blocking, that is, if a socket is dead, you did not delete him in the event team, then he will always be here to block, even if there are events in the future can not be answered, but, If your socket has only one connection, there is nothing that can be changed to infinite waiting. However, it is best not to do this, because if you deal with a mistake, you will have a dead socket (such as a heavy connection, but you did not delete the previously useless socket).
with two:: WSAWaitForMultipleEvents function,
An array of multiple events to handle listening, one to iterate through each array event,
Prevent the phenomenon of loss of response, so its parameter settings are different, it must be noted.
2.2 Handling of events
Then the play came again, the above-mentioned read and write monitoring, it appeared here, including the above ambush of a ray, also handled here:
First Call::wsaenumnetworkevents (Sockarray[i], eventarray[i], &event), the above Ray is torn down,
::wsaenumnetworkevents automatically resets events ,
And then get the index of the event or the ID,
if (event.lnetworkevents & fd_accept)//processing FD_ACCEPT notification messages
{
if (event.ierrorcode[fd_accept_bit] = = 0)
{
if (Neventtotal > Wsa_maximum_wait_events)
{
printf ("Too many connections! \ n ");
Continue
}
SOCKET snew =:: Accept (sockarray[i], NULL, NULL);
Wsaevent event =:: Wsacreateevent ();
:: WSAEventSelect (Snew, event, fd_read| fd_close| Fd_write);
Add to Table
Eventarray[neventtotal] = event;
Sockarray[neventtotal] = snew;
neventtotal++;
}
}
The event creation and event binding functions are recalled in the code, and the two arrays are automatically incremented, most importantly we finally see,fd_read| fd_close| Fd_write,
Understand, the essence of this simple program is actually to mix the read-write and receive-closed sockets together ,
And in the later server routines, we found that this has been opened, and re-manually set the trusted event, called: ResetEvent (Event). This is not the perfect demolition of the above Ray.
2.3 Other treatment methods
When the program continues to loop to the outermost layer,:: WSAWaitForMultipleEvents Infinite waits for all events, as long as there is an event response, it will enter the next layer of the loop, if it is received to repeat the above actions, if read and write to enter:
else if (event.lnetworkevents & Fd_read)//handling Fd_read notification messages
{
if (event.ierrorcode[fd_read_bit] = = 0)
{
Char sztext[256];
int nrecv =:: Recv (Sockarray[i], Sztext, strlen (Sztext), 0);
if (Nrecv > 0)
{
SZTEXT[NRECV] = ' + ';
printf ("Received data:%s \ n", Sztext);
}
}
}
else if (event.lnetworkevents & fd_close)//handling Fd_close notification messages
{
if (event.ierrorcode[fd_close_bit] = = 0)
{
:: Closesocket (Sockarray[i]);
for (int j=i; j<neventtotal-1; j + +)
{
SOCKARRAY[J] = sockarray[j+1];
EVENTARRAY[J] = eventarray[j+1];
}
neventtotal--;
}
}
else if (event.lnetworkevents & fd_write)//handling Fd_write notification messages
{
}
In this way, the problem of continuously receiving connections and processing data is not reached.
Here again, many programs on the Web do not deal with multiple events at the same time trusted, on the internet and a variety of materials, but also some use only one:: WSAWaitForMultipleEvents function, but the parameters are set to be restarted, and handle all kinds of events and anomalies with care. There may be no problem with small concurrency and small amounts of data, but with a large number of concurrent data, there may be problems with data loss, not tested, but may be large. Otherwise, it doesn't say traversal calls this function.
2.4 Triggering of Fd_write events
Here must be wordy two Fd_write event trigger, front of all good understanding, mainly is when will trigger this event, we at the beginning only to receive and shut down to listen, why not this fd_write event
Listen to it,
This leads to the following things: (from a netizen that transfer)
The following is an explanation of the Fd_write trigger mechanism in MSDN:
The Fd_write network event is handled slightly differently. An Fd_write network event was recorded when a socket was first connected with Connect/wsaconnect or
Accepted with Accept/wsaaccept, and then after a send fails with wsaewouldblock and buffer space becomes available. Therefore, an application can assume that
Sends is possible starting from the first Fd_write network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure the
Application would find out that sends be again possible when an Fd_write network event is recorded and the associated even T object is set
The Fd_write event is triggered only in the following three scenarios
①client triggers a Fd_write event on the client side when the connection is first established with the server via connect (wsaconnect)
Fd_write event is triggered on server side when ②server accepts a client connection request via accept (wsaaccept)
③send (WSASend)/sendto (wsasendto) Send failure returns WSAEWOULDBLOCK, and the Fd_write event is triggered when there is free space in the buffer
①② is actually the same situation, when the first connection is established, the C/s terminal will trigger a Fd_write event.
Mainly ③ this situation: send out of the data is actually in the Winsock send buffer before sending out, if the buffer is full, then call Send (Wsasend,sendto,wsasendto), will return a Wsaewouldblock error code, next as the data in the send buffer is sent out, in the buffer when there is free space, a Fd_write event will be triggered, it is easy to confuse the fd_write triggered by the premise is The buffer is filled first and then as the data is sent and there is free space, instead of free space in the buffer, which may be problematic as the following call method
else if (event.lnetworkevents & Fd_write)
{
if (event.ierrorcode[fd_write_bit] = = 0)
{
Send (G_sockarray[nindex], buffer, buffersize);
....
}
Else
{
}
}
The problem is that Fd_write is triggered for the first time after the connection is established, and if send sends enough data to fill the buffer, although there is still free space in the buffer, the fd_write will no longer be triggered, and the program will never wait for the network event that can be sent.
For these reasons, when the Fd_write event is received, the program uses a loop or thread to send data continuously until send returns WSAEWOULDBLOCK, indicating that the buffer is full and then exiting the loop or thread.
When there is new free space in the buffer, the Fd_write event is triggered and the program is notified and the data is sent.
Fd_write event handling omitted from the above code fragment
else if (event.lnetworkevents & Fd_write)
{
if (event.ierrorcode[fd_write_bit] = = 0)
{
while (TRUE)
{
Gets the buffer to be sent, which can be the user's input, read from the file, etc.
GetBuffer ....
if (send (G_sockarray[nindex], buffer, buffersize, 0) = = socket_error)
{
The send buffer is full
if (wsagetlasterror () = = Wsaewouldblock)
Break
Else
Errorhandle ...
}
}
}
Else
{
Errorhandle.
Break
}
}
If you are not a large amount of data constantly sending data, it is recommended that you ignore this event, after all, the buffer is not very easy to get full, the result is that your sending event can not be completed.
2.5 Handling of exceptions
It is mainly 0 connections, the problem of handling CPU occupancy, and the listening processing problem when more than 64 events. And it includes the question of the multi-event at the same time when there is no double loop. 2.6 Multi-Threaded server
This can be read Wang Yanping's book, it is clear that it is important to note that in his main service program, the use of the int nret =:: WaitForSingleObject (event, 5*1000), so the following to manually re-set the event, Otherwise, the event cannot be monitored again.
The other difficulty is mainly object-oriented design package to understand, if this figure out what the purpose of encapsulation socket and thread structure, and then according to the book can not be wrong, but to remind one point,
in the thread structure bodyThe first event is a rebuild event, not to be confused with other listening events.
If you make a small frame between two types of code in the book, you can use a thread to listen to the accept and close events, and another thread to listen for less than 64 read and write events, the general small socket communication should be no problem. The important thing is that you have to encapsulate this server and have time to do it.
Iii. Routines (client)
The first piece of code:
DWORD WINAPI Connect (LPVOID lpparam)
{
1th Step: Initialize, create, connect socket//////////////////
Wsadata Wsadata;int err;
Err = WSAStartup (0x0002, &wsadata); if (err!=0) return 1; 0X0002 Representative version 2.0
Socket_client=socket (af_inet,sock_stream,0);
if (socket_client==invalid_socket) {AfxMessageBox ("Create socket Error!\n"); return 1;}
Sockaddr_in Sconnect_pass;
Sconnect_pass.sin_family=af_inet;
Sconnect_pass.sin_addr. S_un. S_ADDR=INET_ADDR ("127.0.0.1");
Sconnect_pass.sin_port=htons (55551);
if (Socket_error==connect (Socket_client, (sockaddr*) &sconnect_pass,sizeof (sockaddr)))
{
AfxMessageBox ("Connection server error \ n");
return 1;
}
Else
{
Put the socket s in "non-blocking mode"
U_long u1=1;//0 to keep the default blocking, non-0 is changed to non-blocking
Ioctlsocket (Socket_client,fionbio, (u_long*) &u1);
Create an Event object--------------①-----------------
Wsaevent clientevent=wsacreateevent ();
if (clientevent==wsa_invalid_event)
{
#ifdef _DEBUG
:: OutputDebugString ("Create event error!\n");
#endif//_DEBUG
AfxMessageBox ("Wsacreateevent () failed,error="%d "\ n");
return 1;
}
--------------② Network Event Registration------------
int Weserror=wsaeventselect (socket_client,clientevent,fd_read| Fd_close);
if (Weserror==invalid_socket)
{
#ifdef _DEBUG
:: OutputDebugString ("Network Event registration error!\n");
#endif//_DEBUG
AfxMessageBox ("WSAEventSelect () failed,error="%d "\ n");
return-1;
}
-----------Ready to work---------------
WSAWaitForMultipleEvents can only wait for 64 events, and if you want more, create additional worker threads
SOCKET Sockarray[wsa_maximum_wait_events]; Wsaevent Eventarray[wsa_maximum_wait_events];
int neventcount = 0;
Sockarray[0]=socket_client; Eventarray[neventcount]=clientevent;
neventcount++;//Event Number +1, the 1th time to wait for 1 events, note that wsawaitformultipleevents parameter 1 is a dynamic
int t=1;//Timeout number
------------Loop Processing-------------
while (1)
{
---------------⑦ waits for the event object--------------
int nindex=wsawaitformultipleevents (neventcount,eventarray,false,40000,false);///Parameter 1: Note is dynamic increase or decrease, can not be fixed dead. Note: Parameter 1 and 2 are essentially the same, But the values are different. If the reference
The number 1 is 1, then the array in parentheses [] is 0
Parameter 3: Any one in Parameter 1 has a message coming in, stop blocking immediately, and run the next action
AfxMessageBox ("Response event, go to next \ n");//Enter at 0, response is the corresponding array tag number
if (nindex==wsa_wait_failed)//------7.1 call failed---------
{
AfxMessageBox ("WSAEventSelect call failed \ n");
break;//exiting while (1) loop
}
else if (nindex==wsa_wait_timeout)//-------7.2 timeout---------
{
if (t<3)
{
AfxMessageBox ("%d", "times out of Time \ n");
t++;
Continue
}
Else
{
AfxMessageBox ("%d" times timeout, exit \ n ");
Break
}
}
---------------7.3 Network Event triggers the working state of the event object handle--------
Else
{
Wsanetworkevents event;//This structure logs network events and corresponding error codes
---------⑧ Network Event Query-----------
Wsaenumnetworkevents (sockarray[nindex-wsa_wait_event_0],null,&event);
Wsaresetevent (Eventarray[nindex-wsa_wait_event_0]);
if (event.lnetworkevents&fd_read)//-------8.2 process fd_read notification message
{
if (event.ierrorcode[fd_read_bit]==0)
{
Char m_recvbuffer[4096];
Pcmd_header PCM = (pcmd_header) m_recvbuffer;
if (recv (Sockarray[nindex-wsa_wait_event_0], (char*) &m_recvbuffer,sizeof (M_recvbuffer), 0) ==socket_error)
{
AfxMessageBox ("Receive failed, exit heavy recv receive!");
Break
}
Else
{
Switch (pcm->ncmd)
{
Case cmd_as_rep_c_machine_login://Obviously this pcm->ncmd, is the ncmd identifier in the login package
{
Parep_c_machine_login cmd = (parep_c_machine_login) PCM;
if (cmd->nstatus==1)
{
AfxMessageBox ("Receive login reply package (Client->server) Status: Success!");
}
Else
{
AfxMessageBox ("Received login reply package (Client->server) Status: Failed!");
}
}
Break
}
}
}
}
else if (event.lnetworkevents&fd_close)//---------8.3 process fd_close notification message
{
if (event.ierrorcode[fd_close_bit]==0)//client shuts down normally
{
Closesocket (Sockarray[nindex-wsa_wait_event_0]);
Wsacloseevent (Eventarray[nindex-wsa_wait_event_0]);
AfxMessageBox ("Socket closed connection \ n");//NOTE: Trigger 7.1 call failed
}
else//client exception closed
{
if (event.ierrorcode[fd_close_bit]==10053)//Right-click on the definition, you can view a lot of error IDs. On-Demand settings (only the client does not have a notification server, it is turned off illegally)
{
Closesocket (Sockarray[nindex-wsa_wait_event_0]);
Wsacloseevent (Eventarray[nindex-wsa_wait_event_0]);
AfxMessageBox ("Service side illegal shutdown connection \ n");//NOTE: Trigger 7.1 call failed
}
}
for (int j=nindex-wsa_wait_event_0;j<neventcount-1;j++)
{
SOCKARRAY[J]=SOCKARRAY[J+1];
EVENTARRAY[J]=EVENTARRAY[J+1];
}
neventcount--;
}
}//End Network Event triggering
}//end while
//////////////////////////////////////////////////////////////////////////
}
AfxMessageBox ("The server has exited. client exits in \ n");
Closesocket (socket_client);
WSACleanup ();
return 0;
}
void Cmydlg::onbnclickedbuttonrun ()
{
Employer
C_machine_login_system cmd;
strcpy (Cmd.smachinecode, "20100904164702750199");//Machine code
CString str;
Str. Format ("%d", cmd.nversion);
if (Send (Socket_client, (char*) &cmd,sizeof (cmd), 0) ==socket_error)
{
#ifdef _DEBUG
:: OutputDebugString ("Send failed: Send machine code!\n");
#endif//_DEBUG
}
}
Here is no longer a detailed analysis, as compared to the server, here will be more simple, it is necessary to note that here can use wsaconnect this function to achieve the purpose of the connection, do not use this thing, of course, if so, you have to send and receive use WSARecv and WSASend function. The main use of overloapped overlapping IO, the use of more simple and clear.
WSAEventSelect Model Programming Detailed