Windows network and communication programming-book notes-Overlapped I/O model

Source: Internet
Author: User

Overlapped I/O model

Compared with other models described, overlapping I/O models provide better system performance.The basic idea of this model is to allow applications to use overlapping data structures to deliver one or more asynchronous I/O requests at a time (so-called overlapping I/O ). After an I/O request is submitted, the event object in the associated overlapped data is triggered, and the application can use the WSAGetOverlappedResult function to obtain the result of the overlapped operation.This is similar to calling the ReadFile and WriteFile functions using overlapping structures.

 

Specific Programming process:

1. Create an event handle table and a buffer object table

2. Each time a socket is created, a buffer object and an event object are created. hEvent members are associated, and the handles of sockets and event objects are placed in the preceding two tables respectively.

3. Create a listening socket and listening event object, and first deliver several asynchronous Accept requests. (Introduction, which must be shipped first. Otherwise, subsequent asynchronous I/O requests cannot be triggered)

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

5. process network events in the HandleIO function and continue to deliver asynchronous I/O requests. (Continue to deliver asynchronous I/O requests as an introduction)

 

Design Ideas of source code in the book:

Declare a socket object first.

// SOCKET object typedef struct_SOCKET_OBJ {SOCKET s; // SOCKET handle intnOutstandingOps; // record the number of overlapping I/O on the SOCKET. Wait for the member to be 0, it indicates that all the I/O operations on the socket are completed before the socket object LPFN_ACCEPTEXlpfnAcceptEx can be released; // the pointer of the Extended Function AcceptEx (only for the listening socket)} SOCKET_OBJ, * PSOCKET_OBJ;

 

Then declare a buffer object

// Buffer object typedef struct _ BUFFER_OBJ {OVERLAPPEDol; // The overlapping structure char * buff; // send/recv/AcceptEx uses the buffer int nLen; // The length of the buff PSOCKET_OBJpSocket; // The socket object intnOperation to which this I/O belongs; // submitted operation type # define OP_ACCEPT 1 // operation type # define OP_READ 2 # define OP_WRITE 3 SOCKETsAccept; // used to save the client socket accepted by AccetpEx (for listening socket only) _ BUFFER_OBJ * pNext;} BUFFER_OBJ, * PBUFFER_OBJ;

Relationship between socket objects and buffer objects:


 

Specific process:

The program only has a single thread.

At the beginning, a thread creates a listening socket, a listening event object, and a listening event buffer. It is used to deliver several asynchronous Accept I/O requests as the reference for subsequent operations.

Then, call the WSAWaitForMultipleEvents function to wait for the corresponding event to be triggered.

After the event is triggered, call the HandleIO function to process the corresponding network event, and re-deliver the corresponding asynchronous I/O request as an introduction.

Note that s members in all asynchronous Accept I/O requests in the program share the program to create a listening socket sListen at the beginning. If the client socket is not closed, each time an asynchronous Accept I/O request is completed, it will continue to deliver a new asynchronous Accpet I/O request and an asynchronous Send request, each time an asynchronous Read I/O request is completed, a new asynchronous Write I/O request is sent.
The I/O request will post a new Read I/O Request. If the client socket is disabled, all other socket objects and buffer objects will be released, except for the resources required by the program to first deliver N asynchronous Accept I/O requests, the program returns to the initialization status.



Source code:

# Define _ WIN32_WINNT 0x0400 # include <windows. h> # include <cstdio> # include "InitSocket. h "# define BUFFER_SIZE 2048 CInitSock InitSock; // initialization has been performed before entering the main function // SOCKET object typedefstruct _ SOCKET_OBJ {SOCKET s; // SOCKET handle int nOutstandingOps; // record the number of overlapping I/O on this socket LPFN_ACCEPTEX lpfnAcceptEx; // the pointer of the Extended Function AcceptEx (only for listening socket)} SOCKET_OBJ, * PSOCKET_OBJ; // buffer object // The ol member variable must be in the first position: typedef struct _ BUFFER_OBJ {OVERLAPPED ol; // The OVERLAPPED structure char * B Uff; // send/recv/AcceptEx the buffer int nLen used; // The length of the buff PSOCKET_OBJ pSocket; // The socket object int nOperation to which this I/O belongs; // submitted operation type # define OP_ACCEPT1 // operation type # defineOP_READ2 # defineOP_WRITE3SOCKET sAccept; // It is used to save the client socket accepted by AccetpEx (only for listening socket) _ BUFFER_OBJ * pNext;} BUFFER_OBJ, * PBUFFER_OBJ; // apply for the SOCKET object PSOCKET_OBJ GetSocketObj (SOCKET s); // release the SOCKET object void FreeSocketObj (PSOCKET_OBJ pSocket ); // apply for the buffer object PBUFFER_OBJ GetBufferObj (PSOCKE T_OBJ pSocket, ULONG nLen); // release the buffer object void FreeBufferObj (PBUFFER_OBJ pBuffer); // find the triggered object PBUFFER_OBJ FindBufferObj (HANDLE hEvent ); // because it is an array and a linked list, you need to re-align void RebuildArray (); // submit to accept the connection BOOL PostAccept (PBUFFER_OBJ pBuffer); // submit to receive BOOL PostRecv (PBUFFER_OBJ pBuffer ); // send the request BOOL PostSend (PBUFFER_OBJ pBuffer); // I/O processing function BOOL HandleIO (PBUFFER_OBJ pBuffer); // global variable HANDLE g_events [WSA_MAXIMUM_WAIT_EVENTS]; // I/ O Event handle array int g_nBufferCount; // Number of valid handles in the preceding array PBUFFER_OBJ g_pBufferHead, g_pBufferTail; // address of the table consisting of Record Buffer objects // main function int main (void) {// create a listening SOCKET, bind it to the local port, and enter the listening mode int nPort = 4567; SOCKET sListen = WSASocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED ); SOCKADDR_IN si; si. sin_family = AF_INET; si. sin_port = ntohs (nPort); si. sin_addr.s_addr = INADDR_ANY; bind (sListen, (sockaddr *) & si, sizeof (si); listen (sL Isten, 200); // create a SOCKET_OBJ object PSOCKET_OBJ pListen = GetSocketObj (sListen) for the listening socket; // load the Extended Function AcceptExGUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD dwBytes; WSAIoctl (pListen-> s, signature, & GuidAcceptEx, sizeof (GuidAcceptEx), & pListen-> signature, sizeof (pListen-> lpfnAcceptEx), & dwBytes, NULL, NULL ); // create an event object g_events [0] = WSACreateEvent () for re-establishing the g_events array; // multiple I/O requests can be shipped here, the program always maintains five asynchronous Ccept I/Ofor (int I = 0; I <5; ++ I) {PostAccept (GetBufferObj (pListen, BUFFER_SIZE )); // GetBufferObj contains an increase in the event object count} while (TRUE) {// int nIndex = WSAWaitForMultipleEvents (g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE ); int nIndex = WSAWaitForMultipleEvents (g_nBufferCount + 1, g_events, FALSE, 5000, FALSE); // wait for 5 seconds to print connections if (nIndex = WSA_WAIT_FAILED) {printf ("WSAWaitForMultipleEvents () failed \ n"); break;} Else if (nIndex = WSA_WAIT_TIMEOUT) // timeout, I added it myself. The test uses {printf ("current connections: % d \ n", g_nBufferCount); continue ;} nIndex = nIndex-WSA_WAIT_EVENT_0; for (int I = 0; I <= nIndex; ++ I) {int nRet = WSAWaitForMultipleEvents (1, & g_events [I], TRUE, 0, FALSE); // view the trigger object one by one if (nRet = WSA_WAIT_TIMEOUT) {continue;} else {WSAResetEvent (g_events [I]); // recreate the g_events array if (0 = I) {RebuildArray (); continue;} // process this I/OPBUFFER_OBJ PBuffer = FindBufferObj (g_events [I]); if (pBuffer! = NULL) {if (! HandleIO (pBuffer) {RebuildArray () ;}}}return 0 ;}// Request Buffer object PBUFFER_OBJ GetBufferObj (PSOCKET_OBJ pSocket, ULONG nLen) {if (g_nBufferCount> WSA_MAXIMUM_WAIT_EVENTS-1) {return NULL;} PBUFFER_OBJ pBuffer = (PBUFFER_OBJ) GlobalAlloc (GPTR, sizeof (BUFFER_OBJ); if (pBuffer! = NULL) {pBuffer-> buff = (char *) GlobalAlloc (GPTR, nLen); pBuffer-> ol. hEvent = WSACreateEvent (); pBuffer-> pSocket = pSocket; // share a listening socket pBuffer-> sAccept = INVALID_SOCKET; // Add the new BUFFER_OBJ to the list, if (NULL = g_pBufferHead) {g_pBufferHead = g_pBufferTail = pBuffer;} else {g_pBufferTail-> pNext = pBuffer; g_pBufferTail = pBuffer ;} g_events [++ g_nBufferCount] = pBuffer-> ol. hEvent; // here there is an increase in the count} return pBuff Er;} // release the buffer object void FreeBufferObj (PBUFFER_OBJ pBuffer) {// remove the BUFFER_OBJ object PBUFFER_OBJ pTest = g_pBufferHead from the list; BOOL bFind = FALSE; if (pTest = pBuffer) // release the header node {g_pBufferHead = g_pBufferTail = NULL; bFind = TRUE;} else {while (pTest! = NULL & pTest-> pNext! = PBuffer) {pTest = pTest-> pNext;} if (pTest! = NULL) // pTest is the previous node of the deleted node {pTest-> pNext = pBuffer-> pNext; if (NULL = pTest-> pNext) // Delete the End Node {g_pBufferTail = pTest;} bFind = TRUE ;}// release the memory space it occupies if (bFind) {g_nBufferCount --; closeHandle (pBuffer-> ol. hEvent); GlobalFree (pBuffer-> buff); GlobalFree (pBuffer) ;}// apply for the SOCKET object PSOCKET_OBJ GetSocketObj (SOCKET s) {PSOCKET_OBJ pSocket = (PSOCKET_OBJ) GlobalAlloc (GPTR, sizeof (SOCKET_OBJ); if (pSocket! = NULL) {pSocket-> s = s;} return pSocket;} // release the socket object void FreeSocketObj (PSOCKET_OBJ pSocket) {if (pSocket-> s! = INVALID_SOCKET) {closesocket (pSocket-> s);} GlobalFree (pSocket);} // find the triggered buffer object PBUFFER_OBJ FindBufferObj (HANDLE hEvent) {PBUFFER_OBJ pBuffer = g_pBufferHead; while (pBuffer! = NULL) {if (pBuffer-> ol. hEvent = hEvent) {break;} pBuffer = pBuffer-> pNext;} return pBuffer;} // because it is an array and a linked list, you need to re-align void RebuildArray () {PBUFFER_OBJ pBuffer = g_pBufferHead; int I = 1; while (pBuffer! = NULL) {g_events [I ++] = pBuffer-> ol. hEvent; pBuffer = pBuffer-> pNext ;}// submit and accept the connection BOOL PostAccept (PBUFFER_OBJ pBuffer) {PSOCKET_OBJ pSocket = pBuffer-> pSocket; if (pSocket-> lpfnAcceptEx! = NULL) {// set the I/O type and add the overlapping I/O count pBuffer on the socket-> nOperation = OP_ACCEPT; pSocket-> nOutstandingOps ++; // deliver this overlapping I/ODWORD dwBytes; pBuffer-> sAccept = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED ); BOOL B = pSocket-> lpfnAcceptEx (pSocket-> s, // The listening socket pBuffer that has called listen-> sAccept. // new connections will be generated on this socket, it must be an unusable 'pbuffer-> buff, // The receiving buffer for the first piece of data. If it is 0, the new connection does not wait for the first data BUFFER_SIZE-(sizeof (sockaddr_in) + 16) * 2), // the size of the first data buffer, The two addresses are not counted. Sizeof (sockaddr_in) + 16, // the size of the local address structure, must be larger than 16 sizeof (sockaddr_in) + 16, // the size of the remote address structure, it must be larger than 16 & dwBytes, // actual size of received data, & pBuffer-> ol); // Overlapped structure if (! B) {if (WSAGetLastError ()! = WSA_IO_PENDING) {return FALSE;} return TRUE;} return FALSE;} // submit and accept BOOL PostRecv (PBUFFER_OBJ pBuffer) {// set the I/O type, increase the overlapping I/O count on the socket pBuffer-> nOperation = OP_READ; pBuffer-> pSocket-> nOutstandingOps ++; // deliver this overlapping I/ODWORD dwBytes; DWORD dwFlags = 0; WSABUF buf; buf. buf = pBuffer-> buff; // reference buf. len = pBuffer-> nLen; if (WSARecv (pBuffer-> pSocket-> s, & buf, 1, & dwBytes, & dwFlags, & pBuffer-> ol, NULL )! = NO_ERROR) {if (WSAGetLastError ()! = WSA_IO_PENDING) {return FALSE ;}} return TRUE ;}// send the request BOOL PostSend (PBUFFER_OBJ pBuffer) {// set the I/O type, increase the overlapping I/O count pBuffer on the socket-> nOperation = OP_WRITE; pBuffer-> pSocket-> nOutstandingOps ++; // deliver this overlapping I/ODWORD dwBytes; DWORD dwFlags = 0; WSABUF buf; buf. buf = pBuffer-> buff; buf. len = pBuffer-> nLen; if (WSASend (pBuffer-> pSocket-> s, & buf, 1, & dwBytes, dwFlags, & pBuffer-> ol, NULL )! = NO_ERROR) {if (WSAGetLastError ()! = WSA_IO_PENDING) {return FALSE ;}} return TRUE ;}// I/O processing function BOOL HandleIO (PBUFFER_OBJ pBuffer) {PSOCKET_OBJ pSocket = pBuffer-> pSocket; // extract the SOCKET_OBJ object pointer from the BUFFER_OBJ object for reference; pSocket-> nOutstandingOps --; // obtain the overlapping operation result DWORD dwTrans; DWORD dwFlags; BOOL bRet = WSAGetOverlappedResult (pSocket-> s, & pBuffer-> ol, & dwTrans, FALSE, & dwFlags); if (! BRet) {// an error occurs on this socket. Therefore, close the socket and remove the buffer object. // If no other I/O requests are thrown, release the buffer object. Otherwise, wait for other I/O requests on the socket to complete if (pSocket-> s! = INVALID_SOCKET) {closesocket (pSocket-> s); pSocket-> s = INVALID_SOCKET;} if (0 = pSocket-> nOutstandingOps) {FreeSocketObj (pSocket );} freeBufferObj (pBuffer); return FALSE;} // No error occurs. process completed I/Oswitch (pBuffer-> nOperation) {case OP_ACCEPT: // receives a new connection and receives the first packet sent by the other party. {// creates a SOCKET_OBJ object PSOCKET_OBJ pClient = GetSocketObj (pBuffer-> sAccept) for the new customer ); // create a BUFFER_OBJ object for sending data. This object releases PBUFFER_OBJ pSend = Get when a socket fails or is disabled. BufferObj (pClient, BUFFER_SIZE); if (NULL = pSend) {printf ("Too much connections! \ N "); FreeSocketObj (pClient); return FALSE;} RebuildArray (); // copy data to the sending buffer pSend-> nLen = dwTrans; memcpy (pSend-> buff, pBuffer-> buff, dwTrans); // print the received data, add your own printf ("received data: % s \ n", pSend-> buff ); // deliver the sent I/O (display the data back to the customer) if (! PostSend (pSend) {// in case of an error, release the preceding two objects FreeSocketObj (pSocket); FreeBufferObj (pSend); return FALSE ;} // continue delivery and accept I/OPostAccept (pBuffer);} break; case OP_READ: // the received data is complete {if (dwTrans> 0) {// create a buffer, to send data. Here we use the original buffer PBUFFER_OBJ pSend = pBuffer; pSend-> nLen = dwTrans; // print the received data and add your own printf ("received data: % s \ n ", pSend-> buff); // delivery I/O (display data back to the customer) PostSend (pSend );} else // socket close {// close a connection printf ("close a connection \ n"); // The socket must be closed first, so that other I/O shipped on this socket will return if (pSocket-> s! = INVALID_SOCKET) {closesocket (pSocket-> s); pSocket-> s = INVALID_SOCKET;} if (pSocket-> nOutstandingOps = 0) {FreeSocketObj (pSocket );} freeBufferObj (pBuffer); return FALSE ;}} break; case OP_WRITE: // Data Transmission completed {if (dwTrans> 0) {// print the data sending message printf ("data sending completed \ n"); // continue to use this buffer to deliver the request pBuffer for receiving data-> nLen = BUFFER_SIZE; postRecv (pBuffer);} else // socket close {// Similarly, you must first close the socket if (pSocket-> s! = INVALID_SOCKET) {closesocket (pSocket-> s); pSocket-> s = INVALID_SOCKET;} if (pSocket-> nOutstandingOps = 0) {FreeSocketObj (pSocket );} freeBufferObj (pBuffer); return FALSE ;}} break ;}return TRUE ;}


Summary:

Although a server based on the Overlapped I/O model can deliver multiple overlapping I/O requests at a time, the scalability of the program is similar to that of other models such as WSAEventSelect. Because after an asynchronous I/O request is sent, the program still needs to call WSAWaitForMultipleEvents at a certain time point to wait for the completion of asynchronous I/O, here, the WSAEventSelect model is faulty.






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.