Windows network and communication programming-Reading Notes-WSAEventSelect Model

Source: Internet
Author: User

WSAEventSelect Model

Winsock provides another usefulAsynchronous Event Notification I/OModel-WSAEventSelect model. Similar to the WSAAsyncSelect model, this model allows applications to receive event-based network notifications on one or more sockets. It is similar to the WSAAsyncSelect model because it also receives network events of the FD_XXX type. However, it does not rely on the message-driven mechanism of Windows, but is notified through the event object handle.

The basic idea of using this model is:Create an event object for a group of network events that interest you, and then call the WSAEventSelect function to associate network events with event objects. When a network event occurs, Winsock triggers the corresponding event object, and the wait function on the event object will return. Then, you can call the WSAEnumNetworkEvents function to obtain the network events..

 

Specific programming process:

1. Create an event handle table and a corresponding socket handle table

2. Each time a socket is created, an event object is created, their handles are put into the two tables above, and WSAEventSelect is called to add their associations.

3. Call WSAWaitForMultipleEvents to wait on all event objects. After this function is returned, we call the WSAWaitForMultipleEvents function for each event in the event handle table to confirm which sockets have network events.

4. process network events and wait on the event object.


Implementation Process of source code in the book:

1. Declare two struct, one of which is a socket object. Typedefstruct _ SOCKET_OBJ {SOCKET s; // SOCKET. The socket handle event is used to call WSAEventSelect and the corresponding event object. // The SOCKET-related event HANDLE ,, the connection between WSAEventSelect and the corresponding socket is to use the sockaddr_in addrRemote event object; // The client address _ SOCKET_OBJ * pNext; // the next address, and the socket object is connected by the linked list} SOCKET_OBJ, * PSOCKET_OBJ;


WSAWaitForMultipleEvents supports a maximum of WSA_MAXIMUM_WAIT_EVENTS objects, while WSA_MAXIMUM_WAIT_EVENTS is defined as 64. Therefore, this I/O model supports up to 64 sockets at the same time in a thread. To use this model to manage more sockets, you need to create additional working threads, WSAWaitForMultipleEvents has more objects. Alternatively, you do not need to create a thread method. Instead, you can use another data structure to store the objects you want to wait for. Set a timeout value and Perform Batch round robin.

Therefore, another struct, thread object, must be declared for the worker thread to manage the corresponding socket object.

// Thread object. Each thread is responsible for managing up to 63 connections. One event object is used to indicate the reconstruction operation typedefstruct _ THREAD_OBJ {HANDLE events [WSA_MAXIMUM_WAIT_EVENTS]; // record the handle of the event object to be waited by the current thread, event object table int nSocketCount; // record the number of sockets processed by the current thread, which can be 63 PSOCKET_OBJ psockheaders at most; // The list of socket objects processed by the current thread. The pSockHeader points to the header PSOCKET_OBJ pSockTail; // pSockTail points to the table tail CRITICAL_SECTION cs; // key code segment variables, in order to synchronize access to this structure. Two threads access the thread object. One is the listening thread, and a new connection/socket object needs to be allocated to the thread object. One is the working thread corresponding to the thread object, which manages the socket object _ THREAD_OBJ * pNext; // points to the next THREAD_OBJ object, in order to connect to a table} THREAD_OBJ, * PTHREAD_OBJ;

Thread:

Main thread:

The main thread is responsible for listening for the arrival of new connections, associating new sockets with new event objects, allocating new socket objects to existing or new thread objects, and at intervals, print "accepted connections" and "current connections ".

 

Worker thread:

The worker thread is responsible for waiting for the socket object owned by the thread object.

Execute the reconstruction operation or process the readable, writable, and closed socket events respectively.

In addition, the event [0] object in the thread object is used to indicate whether the thread needs to reconstruct the ing between the socket object and the event object. Because socket objects are stored as linked lists in thread objects, and event objects are stored as arrays. When an intermediate socket object is released, the index of the event object array does not have a one-to-one relationship with the first socket objects in the linked list. Therefore, you need to move the event object in the event object array.

Mappings between the socket linked list and event object arrays in thread objects and thread objects.

 

Bug:

In addition, there is a Bug in the code in the book. It is the judgment of various events in the HandleIO function. The FD_CLOSE and FD_READ events can actually occur simultaneously, that is, when the client sends data together with the FIN flag. The code in the book separates these two events. Therefore, when "the client sends data together with the FIN flag" occurs, the server does not know that it will keep holding the closed socket object and will not be released, resulting in a certain amount of memory waste.

After that:

Another point is that although the source code is available on the Internet, I feel that it is more practical. Although it took some time, it was very helpful for me to understand this model, and I also learned a little bit about the design idea.

Source code:

# Define _ WIN32_WINNT 0x0400 # include <windows. h> # include <cstdio> # include "InitSocket. h "CInitSock initSock; // The structure of the SOCKET object has been initialized before entering the main function. The s and event members must be placed in the initial position typedefstruct _ SOCKET_OBJ {SOCKET s; // socket HANDLE event; // socket-related event HANDLE sockaddr_in addrRemote; // client address _ SOCKET_OBJ * pNext; // next address} SOCKET_OBJ, * PSOCKET_OBJ; // thread object. Each thread is responsible for managing up to 64 connected typedefstruct _ THREAD_OBJ {HANDLE events [WSA_MAXIMUM_WAIT_EVENTS]; // record the current Handle of the event object to be waited by the thread, event object table int nSocketCount; // record the number of sockets processed by the current thread PSOCKET_OBJ pSockHeader; // list of socket objects processed by the current thread, pSockHeader points to the header PSOCKET_OBJ pSockTail; // pSockTail points to the end of the table CRITICAL_SECTION cs; // key code segment variable, in order to synchronize the access to this structure _ THREAD_OBJ * pNext; // points to the next THREAD_OBJ object, in order to connect to a table} THREAD_OBJ, * PTHREAD_OBJ; // SOCKET object processing function PSOCKET_OBJ GetSocketObj (SOCKET s); // apply for a SOCKET object, initialize its member void FreeSocketObj (PSOCKET_OBJ pSocket); // release a socket object // thread object Processing Function PTHREAD_OBJ GetThreadObj (); void FreeThreadObj (PTHREAD_OBJ pThread); // recreate the events array void RebuildArray (PTHREAD_OBJ pThread) of the thread object ); // insert a socket BOOL InsertSocketObj (PTHREAD_OBJ pThead, PSOCKET_OBJ pSocket) into the socket list of a thread ); // assign a socket object to a idle thread to process void AssignToFreeThread (PSOCKET_OBJ pSocket); // remove a socket object void RemoveSocketObj (PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket); // The Working thread is responsible for processing the customer's I/O Request DWORD WINAPI SeverThread (LPVOID lpParam); // process real I/OBOOL HandleIO (PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket ); // The FindSocketObj function searches for the corresponding socket object PSOCKET_OBJ FindSocketObj (PTHREAD_OBJ pThread, int nIndex) based on the index of the event object in the events array; // global variable PTHREAD_OBJ g_pThreadList; // point to the header CRITICAL_SECTION g_cs of the thread object list; // synchronize access to global variables // LONG g_nTotalConnections maintained by the main thread; // total number of connections, that is, processed, including disconnected LONG g_nCurrentConnections; // current connection count // main function int main (void) {U SHORT nPort = 4567; 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_addr = INADDR_ANY; // All interface addresses if (SOCKET_ERROR = bind (sListen, (sockaddr *) & sin, sizeof (sin) {printf ("Failed bind () \ n "); return-1;} listen (sListen, 200); // creates a listener event object and associates it with the listener socket WSAEVENT event = WSACreateEvent (); WSAEventSelect (sListen, event, FD_ACCEPT | FD_CLOSE); // select the listening socket for the event, and select the connection and exit event InitializeCriticalSection (& g_cs); // process the client connection request and print the status information while (TRUE) {int nRet = WaitForSingleObject (event, 5*1000); if (nRet = WAIT_FAILED) {printf ("Failed WaitForSingleObject () \ n"); break ;} else if (nRet = WSA_WAIT_TIMEOUT) // periodically displays status information {printf ("\ n"); printf ("Total Connections: % d \ n", g_nTotalConnections ); printf ("Current Connections: % d \ n", g_nCurrentConnections );} Else // a connection event occurs, and the listener event is triggered {ResetEvent (event); // process all pending connections cyclically while (TRUE) {sockaddr_in si; int nLen = sizeof (si); SOCKET sNew = accept (sListen, (sockaddr *) & si, & nLen); // because it has been selected by the event, therefore, if (SOCKET_ERROR = sNew) {break;} PSOCKET_OBJ pSocket = GetSocketObj (sNew); pSocket-> addrRemote = si; WSAEventSelect (pSocket-> s, pSocket-> event, FD_READ | FD_CLOSE | FD_WRITE); // Add a new socket object and the corresponding event AssignToFreeThread (pSocket) ;}} DeleteCriticalSection (& g_cs); return 0 ;}// apply for a SOCKET object and initialize its member PSOCKET_OBJ GetSocketObj (SOCKET s) {PSOCKET_OBJ pSocket = (PSOCKET_OBJ) globalAlloc (GPTR, sizeof (SOCKET_OBJ); // The global heap and local heap are not distinguished. HeapAllocif (pSocket! = NULL) {pSocket-> s = s; pSocket-> event = WSACreateEvent ();} return pSocket;} // release the socket object void FreeSocketObj (PSOCKET_OBJ pSocket) {CloseHandle (pSocket-> event); if (pSocket-> s! = INVALID_SOCKET) {closesocket (pSocket-> s);} GlobalFree (pSocket);} // get a thread object PTHREAD_OBJ GetThreadObj () {PTHREAD_OBJ pThread = (PTHREAD_OBJ) GlobalAlloc (GPTR, sizeof (THREAD_OBJ); if (pThread! = NULL) {InitializeCriticalSection (& pThread-> cs); // creates an event object to indicate that the handle array of the thread needs to be rebuilt, pThread-> events [0] = WSACreateEvent (); // Add the newly applied thread object to the list EnterCriticalSection (& g_cs); pThread-> pNext = g_pThreadList; // insert it to the linked list of thread objects g_pThreadList = pThread; LeaveCriticalSection (& g_cs) ;}return pThread ;}// release a thread object void FreeThreadObj (PTHREAD_OBJ pThread) {// find the object indicated by pThread in the thread object list. If found, remove EnterCriticalSection (& g_cs); PTHREAD _ OBJ p = g_pThreadList; if (p = pThread) // if you want to delete the header node {g_pThreadList = p-> pNext;} else {while (p! = NULL & p-> pNext! = PThread) {p = p-> pNext;} if (p! = NULL) {// p is the first of pThread, that is, "p-> pNext = pThread" p-> pNext = pThread-> pNext ;}} leaveCriticalSection (& g_cs); // release the resource CloseHandle (pThread-> events [0]); DeleteCriticalSection (& pThread-> cs); GlobalFree (pThread );} // re-create the events array of the thread object. because event objects are stored in arrays, and socket objects are stored in linked lists, there will be inconsistent mappings when a socket object is closed. void RebuildArray (PTHREAD_OBJ pThread) {EnterCriticalSection (& pThread-> cs); // to synchronously listen to the connection thread's access to this thread object PSOCKET_OBJ pSocket = pThre Ad-> pSockHeader; int n = 1; // write from 1st, and 0th are used to indicate that the while (pSocket! = NULL) {pThread-> events [n] = pSocket-> event; pSocket = pSocket-> pNext; n ++;} LeaveCriticalSection (& pThread-> cs );} // insert a socket BOOL InsertSocketObj (PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket) to the socket list of a thread {BOOL bRet = FALSE; EnterCriticalSection (& pThread-> cs ); if (pThread-> nSocketCount <WSA_MAXIMUM_WAIT_EVENTS-1) // The maximum number of event objects that a thread can wait for {if (NULL = pThread-> pSockHeader) // The socket list of threads is null {pThread-> pSockHeader = pThrea D-> pSockTail = pSocket;} else {pThread-> pSockTail-> pNext = pSocket; pThread-> pSockTail = pSocket;} pThread-> nSocketCount ++; bRet = TRUE ;} leaveCriticalSection (& pThread-> cs); // if (bRet) {InterlockedIncrement (& g_nTotalConnections ); // InterlockedIncrement (& g_nCurrentConnections);} return bRet;} // schedule a socket object to a idle thread for void AssignToFreeThread (PSOCKET_OBJ pSocket) {pSocket-> pNext = NUL L; EnterCriticalSection (& g_cs); PTHREAD_OBJ pThread = g_pThreadList; // The thread object linked list header // tries to insert it into the existing thread while (pThread! = NULL) {if (InsertSocketObj (pThread, pSocket) {break;} pThread = pThread-> pNext;} // No idle thread, create a new thread for this socket if (NULL = pThread) {pThread = GetThreadObj (); InsertSocketObj (pThread, pSocket); CreateThread (NULL, 0, SeverThread, pThread, 0, NULL); // beginthreadex is used. A thread object corresponds to a thread, and the thread parameter is the thread object corresponding to the thread} LeaveCriticalSection (& g_cs ); // instruct the new thread to reconstruct the handle array WSASetEvent (pThread-> events [0]);} // remove a socket object void RemoveSocketObj (PT HREAD_OBJ pThread, PSOCKET_OBJ pSocket) {EnterCriticalSection (& pThread-> cs); // query the specified socket object in the socket Object List, remove PSOCKET_OBJ pTest = pThread-> pSockHeader; if (pTest = pSocket) // Delete the header node {if (pThread-> pSockHeader = pThread-> pSockTail) // there is only one node {pThread-> pSockTail = pThread-> pSockHeader = pTest-> pNext ;} else // There are not only one head node {pThread-> pSockHeader = pTest-> pNext ;}} else {while (pTest! = NULL & pTest-> pNext! = PSocket) {pTest = pTest-> pNext;} if (pTest! = NULL) {if (pThread-> pSockTail = pSocket) // Delete the End Node {pThread-> pSockTail = pTest ;} pTest-> pNext = pSocket-> pNext;} pThread-> nSocketCount --; LeaveCriticalSection (& pThread-> cs); WSASetEvent (pThread-> events [0]); // indicates that the thread re-creates the handle array InterlockedDecrement (& g_nCurrentConnections); // indicates that a connection is interrupted} // The Working thread is responsible for processing the customer's I/O Request DWORD WINAPI SeverThread (LPVOID lpParam) {// get the pointer PTHREAD_OBJ pThread = (PTHREAD_OBJ) lpParam; whi Le (TRUE) {// wait for the network event int nIndex = WSAWaitForMultipleEvents (pThread-> nSocketCount + 1, pThread-> events, FALSE, WSA_INFINITE, FALSE); nIndex = nIndex-WSA_WAIT_EVENT_0; // is the index triggered here the lowest? // Check whether the event object for (int I = nIndex; I <pThread-> nSocketCount + 1; ++ I) {nIndex = WSAWaitForMultipleEvents (1, & pThread-> events [I], TRUE, 1000, FALSE ); // only wait for a single event object if (nIndex = WSA_WAIT_FAILED | nIndex = WSA_WAIT_TIMEOUT) {continue;} else // if an event triggers {if (0 = I) // events [0] has been triggered. re-create the Array {RebuildArray (pThread); // if no client I/O has to be processed, then this thread exits if (0 = pThread-> nSocketCount) {FreeThreadObj (pThread); re Turn 0;} WSAResetEvent (pThread-> events [0]); // reset event correspondence} else // process network events {// find the corresponding socket Object Pointer, call HandleIO to process network events. index I is used for search, so we need to recreate the array before. PSOCKET_OBJ pSocket = (PSOCKET_OBJ) FindSocketObj (pThread, I); if (pSocket! = NULL) {if (! HandleIO (pThread, pSocket) // when HandleIO returns FALSE, it indicates that the socket is closed or an error occurs. Although the array is rebuilt {RebuildArray (pThread );}} else {printf ("Unable to find socket object \ n") ;}}} return 0 ;} // The FindSocketObj function searches for the corresponding socket object PSOCKET_OBJ FindSocketObj (PTHREAD_OBJ pThread, int nIndex) based on the index of the event object in the events array) // nIndex starts from 1 {// query PSOCKET_OBJ pSocket = pThread-> pSockHeader in the socket list; while (-- nIndex) // reverse query reason {if (NULL = pSocket) {return NULL;} pSocket = p Socket-> pNext;} return pSocket;} // process the real I/OBOOL HandleIO (PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket) {// obtain the specific network event WSANETWORKEVENTS event; WSAEnumNetworkEvents (pSocket-> s, pSocket-> event, & event); // binds the socket to a network event. do {if (event. lNetworkEvents & FD_READ) {if (event. iErrorCode [FD_READ_BIT] = 0) {char szText [256]; int nRecv = recv (pSocket-> s, szText, sizeof (szText), 0); // sizeof? If (nRecv> 0) {szText [nRecv] = '\ 0'; printf ("received data: % s \ n", szText );}} else {break;} if (event. lNetworkEvents & FD_CLOSE) // You must reduce the number of connections here. You cannot/* else if */. This is the case in the book. As a result, the connection {if (event. iErrorCode [FD_CLOSE_BIT] = 0) {printf ("close a connection \ n"); break;} else {printf ("An error occurred while closing the connection \ n "); break;} if (event. lNetworkEvents & FD_WRITE) // else if {if (event. iErrorCode [FD_WRITE_BIT] = 0) {} else {break;} return TRUE;} while (FALSE); // If the socket is disabled or an error occurs, the program will go here to execute RemoveSocketObj (pThread, pSocket); FreeSocketObj (pSocket); return FALSE ;}

Summary:

Although the WSAEventSelect model is more flexible than the select model and the WSAAsyncSelect model, for thousands of connections, Servers Based on the WSAEventSelect model may create too many threads, what follows is the huge cost of context switching between working threads. In addition, if the client is disconnected for a short period of time after the connection, it will also cause the server to create and destroy a large number of threads in a short period of time, which also brings a certain amount of overhead.












Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.