1. Advantages of the overlap model 2. Basic principles of the overlap model 3. basic knowledge about the overlap model 4. Implementation steps of the overlap model 5. Considerations for multi-client scenarios
I. Advantages of overlapping models
1. It can run on all Windows platforms that support Winsock2, and does not support the NT System as the complete port.
2. Compared with blocking, select, wsaasyncselect, and wsaeventselect models, the overlapped I/O model enables applications to achieve better system performance.
Because it is different from the four models, applications using overlapping models notify the buffer sending and receiving system to directly use data, that is, if the application delivers a 10 KB buffer to receive data, and the data has reached the socket, the data will be directly copied to the delivery buffer.
In these four models, the data arrives and is copied to the single-socket receiving buffer. At this time, the application will be notified of the capacity that can be read. After the application calls the receiving function, the data is copied from the single socket buffer to the buffer zone of the application. The difference is shown.
3. from the test results provided in Windows Network Programming, we can see that in the response server that uses the P4 1.7g Xero processor (strong CPU) and MB, A maximum of 40 thousand socket connections can be processed, and the CPU usage is only about 10 thousand when 40% connections are processed-very good performance, which has been forced to complete the port.Ii. Basic principles of overlapping models
With so many advantages, you must be eager to try it, but we still need to mention the basic principles of the overlapping model first.
To sum up, the overlap model allows an application to use the overlapping data structure (wsaoverlapped) to deliver one or more Winsock I/O requests at a time. After these submitted requests are completed, the application receives a notification, so it can process the data through its own code.
Note that there are two methods to manage the completion of overlapping IO requests (that is to say, a notification is received for the completion of overlapping operations ):
1. event object notification)
2. Completion routines. Note that this is not the completion port.
This article only describes how to use the event notification method to implement the overlapping Io model, and puts the method preparation for completing the routine in the next article. (There is too much content to write ), unless otherwise stated, the overlap model in this article is the event notification-based overlap model by default.
Since it is based on event notification, it is required to associate Windows event objects with the wsaoverlapped structure (wsaoverlapped structure has corresponding parameters .... By the way, I forgot to mention that since we want to use overlapping structures, our commonly used send, sendto, Recv, and recvfrom will also be replaced by wsasend, wsasendto, wsarecv, and wsarecvfrom, I will talk about their usage later. Here, you only need to note that all their parameters have an overlapped parameter, we can assume that we have "bound" our operations such as wsarecv to this overlapping structure and submitted a request. Other things will be handled by the overlapping structure, the overlapping structure must be "Bound" with the Windows event object, so that we can "Enjoy it" after calling wsarecv. After the overlapping operation is completed, there will naturally be corresponding events to notify us of the completion of the operation, and then we can obtain the desired data based on the results of the overlapping operations.
Maybe you haven't understood it for a long time, so let's continue watching it later ....... -_-B. The language expression capability is limited ~~~
III. Basic knowledge about overlapping models
The following describes and provides examples of several key functions that will be used in programming overlapping models.
1. wsaoverlapped Structure
This structure is naturally the core of the overlapping model, which is defined in this way.
Typedef struct _ wsaoverlapped {
DWORD internal;
DWORD internalhigh;
DWORD offset;
DWORD offsethigh;
Wsaevent hevent; // the unique parameter to be followed, used to associate wsaevent objects} wsaoverlapped, * lpwsaoverlapped; we need to ship wsarecv and other operations to an overlapping structure, in addition, we need an event object that is "Bound" with the overlapping structure to notify us of the completion of the operation. We can see that it is consistent with the hevent parameter, I don't need to say that you should know how to bind event objects to the overlapping structure? Roughly as follows: wsaevent event; // defines the event wsaoverlapped acceptoverlapped; // defines the overlapping structure event = wsacreateevent (); // creates an event object handle zeromemory (& acceptoverlapped, sizeof (wsaoverlapped); // initializes the acceptoverlapped of the overlapped structure. hevent = event; // done !!
2. wsarecv series Functions
In the overlap model, it depends on the received data, and its parameters are more than the Recv, because it is defined as follows:
Int wsarecv (socket S, // Of course, it is the socket lpwsabuf lpbuffers that ships this operation, // The receiving buffer, which is different from the Recv function // an array consisting of wsabuf structures DWORD dwbuffercount, // Number of wsabuf structures in the array lpdword lpnumberofbytesrecvd. // if the receiving operation is completed immediately, the function call is returned. // The number of received bytes is lpdword lpflags. // it takes a long time, here we can set it to 0 to lpwsaoverlapped lpoverlapped, // The parameters that will be used in the "bind" overlapping structure lpwsaoverlapped_completion_routine lpcompletionroutine // completion routine. Here we set it to null); Return Value: wsa_io_pending: the most common return value indicates that the wsarecv operation is successful, but the I/O operation is not completed yet. Therefore, we need to bind an event to notify us when the operation will be completed.
For example: (the order in which variables are defined corresponds to the sequence described above, the same below) socket s; wsabuf databuf; // define the buffer of the wsabuf structure // initialize databuf # define data_bufsize 5096 char buffer [data_bufsize]; zeromemory (buffer, data_bufsize); databuf. len = data_bufsize; databuf. buf = buffer; DWORD dwbuffercount = 1, dwrecvbytes = 0, flags = 0; // create the required overlapping structure wsaoverlapped acceptoverlapped; // If You Want To process multiple operations, here, of course, you need a // wsaoverlapped array wsaevent event; // if you want multiple events, you also need A wsaevent array // note that a socket may have more than one overlapping request at the same time, // it will correspond to more than one wsaevent event = wsacreateevent (); zeromemory (& acceptoverlapped, sizeof (wsaoverlapped); acceptoverlapped. hevent = event; // The key step is to "bind" the event handle to the overlapping structure. // after so much work, finally, we can use wsarecv to deliver our requests to the overlapping structure, calling .... Wsarecv (S, & databuf, dwbuffercount, & dwrecvbytes, & flags, & acceptoverlapped, null); I will not describe other functions here, because we have such a good helper as msdn after all, in addition, I will also talk about the completion routine and the completion port later.
3. wsawaitformultipleevents Function
Friends who are familiar with the wsaeventselect model are certainly not unfamiliar with this function. No one should be familiar with it. This function is similar to the commonly used waitformultipleobjects function in threads in some places, because they are waiting for the trigger of an event.
Because we need events to notify us of the completion of overlapping operations, we naturally need to wait for the function of the event to match it.
DWORD wsawaitformultipleevents (DWORD cevents, // total number of pending events const wsaevent * lphevents, // pointer to the event array bool fwaitall, // set to true, the function returns the result when all events in the event array are sent/false. Then, the function returns the result when any event is sent. // we must set the DWORD dwtimeout to false., // timeout time. If it times out, the function returns wsa_wait_timeout // if it is set to 0, the function will return immediately // if it is set to wsa_infinite, it will return only after an event is sent // It is not recommended to set it to wsa_infinite here, Because... Let's talk about it later .. -_-B bool falertable // this parameter is used in the completion routine. Here we first set it to false); Return Value: wsa_wait_timeout: The most common return value, what we need to do is to continue wait wsa_wait_failed: an error occurs. Check whether the parameters cevents and lphevents are valid. If an event in the event array is sent, the function returns the index value of the event, but the index value minus the predefined value wsa_wait_event_0 is the position of the event in the event array. The specific example will not be mentioned here. It will be noted later that the wsawaitformultipleevents function can only support a maximum value defined by the wsa_maximum_wait_events object, which is 64, that is, wsawaitformultipleevents can only wait for 64 events, if you want to wait for more than 64 events at the same time, you need to create additional worker threads and have to manage a thread pool. This is not as good as the completion routine model described in the next article.
4. wsagetoverlappedresult Function
Since we can use the wsawaitformultipleevents function to get the notification of overlapping operations, we naturally need a function to query the results of the overlapping operations. The definition is as follows:
Bool wsagetoverlappedresult (socket S, // socket, needless to say lpwsaoverlapped lpoverlapped, // here is the pointer to the overlapped structure of the query result lpdword lpcbtransfer, // The actual number of bytes received (or sent) for this overlap operation bool fwait, // set to true, unless the overlap operation is completed, the function will not return // set false, if the operation is still suspended, the function will return false // The error is wsa_io_incomplete //. However, because we are waiting for the event to send a message to notify us that the operation is complete, so what we set here does not work... Lpdword lpdwflags // pointer to DWORD to receive the result flag); this function is not difficult. Here we do not need to pay attention to its return value, you can directly fill in the parameters for the call. Here we will not give an example. The only thing you need to note is that if wsagetoverlappedresult is complete and the third parameter returns 0, the communication peer has closed the connection, the socket and event on our side can be closed.
4. Steps for implementing overlapping models
After so much preparation and so much ink, we can start coding. As a matter of fact, it may take a little effort to understand the internal principle of the overlapping structure, but it is really not difficult to learn how to use it, the only thing that needs to be clarified is how to know which overlapping operation is completed after we receive event notifications when we interact with a large number of clients, then you can know which socket to process and which buffer zone to retrieve data. Everything will be OK ^_^.
Next we will work with the code to explain how to complete an overlap model step by step.
[Step 1] define variables ............
# Define data_bufsize 4096 // receiving buffer size socket listensocket, // listening socket acceptsocket; // socket for communication with the client wsaoverlapped acceptoverlapped; // overlapping structure a wsaevent eventarray [accept]; // event handle array wsabuf databuf [data_bufsize] used to notify the completion of overlapping operations; DWORD dweventtotal = 0, // The total number of events in the program dwrecvbytes = 0, // The received character length is flags = 0; // wsarecv Parameter
[Step 2] Create a socket and start listening for connection requests on the specified port
It is no different from other socket initialization methods. You can simply copy them directly, and there is no extra effort here. It should be noted that, in order to be clear, I have removed the error handling and should not normally do this, although the error rate here is relatively small.
Wsadata; wsastartup (makeword (2, 2), & wsadata );
Listensocket = socket (af_inet, sock_stream, ipproto_tcp); // create a TCP socket
Sockaddr_in serveraddr; // allocate the port and protocol family and bind serveraddr. sin_family = af_inet; serveraddr. Handler = htonl (inaddr_any); serveraddr. sin_port = htons (11111 );
BIND (listensocket, (lpsockaddr) & serveraddr, sizeof (serveraddr); // bind a socket
Listen (listensocket, 5); // start listening
Step 3: accept an inbound connection request
Acceptsocket = accept (listensocket, null, null); of course, I am lazy here. If you want to obtain the information that is connected to the client (I remember someone asked on the Forum ), do not use NULL for the last two parameters of accept.
Sockaddr_in clientaddr; // defines the address structure of a client as the parameter int addr_length = sizeof (clientaddr); acceptsocket = accept (listensocket, (sockaddr *) & clientaddr, & addr_length ); // Therefore, we can easily learn the information of the connected client. sin_addr); // ip uint nport = clientaddr. sin_port; // Port
Step 4: Create and initialize overlapping structures
Create a new wsaoverlapped overlapping structure for the connected socket, and pick out an idle object handle "Bound" from the event handle array for this overlapping structure as mentioned earlier.
// Create an event // dweventtotal can be used as the index eventarray [dweventtotal] = wsacreateevent () of the event array temporarily ();
Zeromemory (& acceptoverlapped, sizeof (wsaoverlapped); // set zero acceptoverlapped. hevent = eventarray [dweventtotal]; // associate events
Char buffer [data_bufsize]; zeromemory (buffer, data_bufsize); databuf. Len = data_bufsize; databuf. Buf = buffer; // initialize a wsabuf structure dweventtotal ++; // Add one to the total number
Step 5: deliver the wsarecv request on the socket with the wsaoverlapped structure as the parameter
After all the variables have been initialized, we can start the socket operation, and then let the wsaoverlapped structure manage the I/O Request for us. We only need to wait for the event to trigger.
If (wsarecv (acceptsocket, & databuf, 1, & dwrecvbytes, & flags, & acceptoverlapped, null) = socket_error) {// it is normal to return wsa_io_pending, it indicates that the IO operation is in progress and cannot be completed immediately. // if it is not a wsa_io_pending error, it will be very difficult ~~~~~~!!! If (wsagetlasterror ()! = Wsa_io_pending) {// you can only close closesocket (acceptsocket); wsacloseevent (eventarray [dweventtotal]);}
Step 6: Use the wsawaitformultipleevents function to wait for the result returned by the overlap operation.
We have previously assigned an event object handle to the overlapping structure associated with wsarecv. Therefore, we need to wait for the event object to be triggered in concert with it, in addition, you need to determine which event in the event array is triggered Based on the return value of the wsawaitformultipleevents function. For the usage and return value of this function, refer to the previous basic knowledge section.
DWORD dwindex; // wait for the overlapping I/O call to end // because we bind the event with overlapped, we will receive an Event Notification dwindex = wsawaitformultipleevents (dweventtotal, eventarray, false, wsa_infinite, false); // note that the returned index is not the index of the event in the array, but must be subtracted from wsa_wait_event_0 dwindex = dwindex-wsa_wait_event_0;
Step 7: Use the wsaresetevent function to reset the currently used event object
Once an event has been triggered, it has no use value for us, so we need to reset it for the next use. It is very simple, just one step, and even the return value does not need to be considered.
Wsaresetevent (eventarray [dwindex]);
Step 8: Use the wsagetoverlappedresult function to obtain the returned status of overlapping calls.
This is what we are most concerned about. What is the result of this overlapping operation that requires a great deal of shipping? In fact, for this model, the only thing that needs to be checked is whether the socket connection of the other party has been closed.
DWORD success; wsagetoverlappedresult (acceptsocket, acceptoverlapped, & dwbytestransferred, false, & flags); // first check whether the communication peer has closed the connection. // if it is = 0, the connection has been established, disable socket If (dwbytestransferred = 0) {closesocket (acceptsocket); wsacloseevent (eventarray [dwindex]); // Close event return ;}
Step 9: "enjoy" the received data
If the program is executed here, it means everything is normal. The wsabuf structure contains data from our wsarecv and it is time to enjoy the results! Have a cup of tea and have a rest ~~~ Pai_^
Databuf. Buf is a char * string pointer. Let me leave it alone.
Step 10: Send the wsarecv request on the socket, as in step 5. Repeat Step 6 ~ 9
In this way, we can finally receive data from the client, but in retrospect ~~~~~, Isn't it possible to receive data only once, and then the program will not be over ?....... -_-B. Therefore, we have to repeat Step 4 and Step 5 to deliver another wsarecv request on this socket and make the entire process loop, are u clear ??
You can refer to my code, so I will not write it here, because you must be more smart than me. After understanding the key, you can think a little more flexibly.
5. Client considerations
After completing the above loop, the overlap model has basically been set up to 80%. Why not 100%? Because, in retrospect ~~~~~~~, Isn't it possible to connect only one client ?? Yes, if you only process one client, the overlap model has no advantage at all. We just want to use the overlap model to process multiple clients.
So we have to make some changes to the structure.
1. First, a socket array must be used to communicate with each socket separately.
This is because each socket operation in the overlap model needs to be "Bound" with an overlapping structure. Therefore, a wsaoverlapped structure is required for each socket operation. However, this is not strict, because if each socket has only one operation at the same time, for example, wsarecv, a socket can correspond to a wsaoverlapped knot, but if a socket has two operations: wsarecv and wsasend, therefore, a socket must correspond to two wsaoverlapped structures, so how many socket operations will have multiple wsaoverlapped structures.
Then, a wsaevent event is required for each wsaoverlapped structure. Therefore, the number of socket operations should be the number of wsaoverlapped structures, the number of wsaoverlapped structures should be as many as the number of wsaevent events. It is best to associate socket-wsaoverlapped-wsaevent so that it will not be messy until the critical moment is reached :)
2. Two threads have to be divided:
A connection used for loop listening, receiving requests, and then sending the first wsarecv request to the socket with a wsaoverlapped structure, and then entering the second thread waiting for the Operation to complete.
The second thread is used to keep the wsaevent array wsawaitformultipleevents, wait for the completion of any overlapping operation, and then process it according to the returned index value. After the processing is complete, it will continue to deliver another wsarecv request.
Note that we set the parameter of the wsawaitformultipleevents function to WSA _
Infinite, but it is not OK when there are multiple clients. You need to set a timeout time. If you wait for the timeout and then re-run wsawaitformultipleevents, because the wsawaitformultipleevents function is blocked when it is not triggered, we can imagine that if the listening thread accesses a new connection, it will naturally add an event for the connection, however, wsawaitformultipleevents is still blocked and will not process the event of the new connection. I do not know whether it means that there is no ...... -_-B may not be available here, and you will understand it when coding it.
For other things you may not understand, refer to my code. There are also detailed comments in the Code. Enjoy ~~~
However, it is a pity that the code of MFC is used to take care of most people, and the code is a little messy.
6. Known issues
This known issue refers to the known issue in my code, but not the known issue of overlapping structures :)
This sample code has been written for a long time. During the last test in the past two days, we found that there were actually two bugs, but not every time, 5555, recently, I have no energy to change these bugs. If you are interested in fixing these two bugs, it will benefit you. This article is almost aborted, I have never experienced any bugs in code that I almost forgot to modify. I wrote them here to remind you that the code is just a reference, in addition, I think the text is more precious than the code ^ _ ^, because there are still a lot of overlapping model code online. The two bugs are as follows:
1. Exceptions may occur when multiple clients exit consecutively;
2. Sometimes the receiving buffer of multiple clients will overlap, that is to say, the data sent by Client A will be followed by the data sent by client B last time ..... -_-B
Improved Algorithms: in fact, there are still many improvements to the algorithms in the Code. Limin mentioned several very good improved algorithms to me, such as how to find idle sockets in the socket array for communication, but I didn't add it to this Code, because the code of the overlapping model is complicated. I am afraid that adding these things will bring difficulties for beginners. However, you are welcome to discuss with me the algorithm for improving overlapping models and the problems in my code! Pai_^
Overlapping Io Model