Understanding the asynchronous notification I/O model
Synchronous and asynchronous
The key of synchronization is the call and return time of function, as well as the start and completion time of data transmission.
- Data transmission starts at the moment when the send function is called, and the data transmission is completed at the moment when the send function is executed (returned) (data is completely transmitted to the output buffer, and the operating system completes network transmission).
- The moment when the recv function is called starts to receive data, and the moment when the recv function is completed (returned).
Asynchronous, inconsistent.
Asynchronous I/O means that the return time of I/O function is inconsistent with the completion time of data sending and receiving.
Disadvantages of synchronous I/O and asynchronous solutions
During I/O synchronization, functions cannot return and cannot perform other tasks.
Asynchronous I/O immediately returns functions, which can perform other tasks and use the CPU more effectively.
Asynchronous notification I/O model
The asynchronous working mode is "Notification I/O".
Notify I/O: notify that the input buffer receives data and needs to be read, and that the output buffer is empty, so data can be sent.
Select notifies I/O in a synchronous manner. The time point at which I/O is required or available (the time point at which I/O related events occur) is consistent with the return time point of the select function.
In asynchronous notification I/O, the function that specifies the I/O monitoring object and the function that actually verifies the state change are separated from each other. After specifying the monitoring object, you can leave to perform other tasks, and finally come back to verify the state change.
Implement asynchronous notification I/O model
WSAAsyncSelect function, you need to specify a Windows handle to get the events (UI related content), which will not be covered in this book.
WSAEventSelect function
I/O status change
- I/O state change of socket
- Socket I/O related events occurred
#include <winsock2.h> //Success 0, failure SOCKET_ERROR int WSAEventSelect( SOCKET s, WSAEVENT hEventObject, //Event object handle long lNetworkEvents //Monitored event type information, or operation );
As long as one of the events specified in lNetworkEvents occurs in the socket passing in parameter s, the WSAEventSelect function changes the kernel object referred to by the hEventObject handle to the signaled state. This function is a function connecting the event object and socket. It works in the form of asynchronous notification and returns directly after calling.
Event type information:
- FD_READ: readable
- FD_WRITE: writable
- FD_OOB: out of band data received
- FD_ACCEPT: new connection
- FD_CLOSE: disconnect
Creation of event object in manual reset mode
The CreateEvent function can create an auto reset or manual reset mode event object.
Wsa createevent only creates event objects in manual reset mode and non signaled state.
#include <winsock2.h> //#define WSAEVENT HANDLE //Failed WSA_INVALID_EVENT WSAEVENT WSACreateEvent(void); //Destroy the event object created by the above function BOOL WSACloseEvent(WSAEVENT hEvent);
Verify that an event has occurred
#include <winsock2.h> //Failed WSA_INVALID_EVENT DWORD WSAWaitForMultipleEvents( DWORD cEvents,//The number of event objects that verify whether they are transferred to the signaled state const WSAEVENT *lphEvents,//Event object handle array BOOL fWaitAll,//If TRUE, only the signaled status of all event objects is returned; FALSE, the signaled status of any event object is returned DWORD dwTimeout,//Timeout (1 / 1000 second), WSA_INFINITE has been waiting BOOL fAlertable//When TRUE, enter the alertable wait state ); //Return value minus WSA_WAIT_EVENT_0, get the corresponding index of the event object handle converted to the signaled state (event object handle array). //Multiple signaled state event objects get smaller values (only one signaled state event object handle can be obtained at a time). //Timeout return WAIT_TIMEOUT
Up to 64 event objects can be passed. If you need to monitor more handles, you can only create a thread or extend the array that holds handles, and call the above function multiple times.
Gets the event object for all signaled states.
int posInfo, startIdx, i; posInfo = WSAWaitForMultipleEvents(nmuOfSock, hEventArray, FALSE, WSA_INFINITE, FALSE); startIdx = posInfo-WSA_WAIT_EVENT_0; for(i=startIdx; i<numOfSock, i++) { int sigEventIdx = WSAWaitForMultipleEvents(1, &hEventArray[i], TRUE, 0, FALSE)-WSA_WAIT_EVENT_0; }
Distinguish event types
#include <winsock2.h> //Success 0, failure SOCKET_ERROR int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, //The event object handle of the signaled state associated with the socket LPWSANETWORKEVENTS lpNetworkEvents//Save event type information and error information );
The above function changes the manual reset mode event object to the non signaled state without calling the ResetEvent function separately.
type struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS]; } WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
View the type of event that occurred
WSANETWORKEVENTS netEvents; WSAEnumNetworkEvents(hSock, hEvent, &netEvents); if(netEvents.lNetworkEvents & FD_ACCEPT) { // } if(netEvents.lNetworkEvents & FD_READ) { // } if(netEvents.lNetworkEvents & FD_WRITE) { // } if(netEvents.lNetworkEvents & FD_CLOSE) { // }
The iErrorCode array holds error information.
- FD_ Read error, view iErrorCode[FD_READ_BIT]
- FD_ Write error, view iErrorCode[FD_WRITE_BIT]
- FD_ XXX error, view iErrorCode[FD_XXX_BIT]
WSANETWORKEVENTS netEvents; WSAEnumNetworkEvents(hSock, hEvent, &netEvents); if(netEvents.iErrorCode[FD_READ_BIT] != 0) { // }
Asynchronous notification I/O model echo server
AsyncNotiEchoServ_win.c
#include <stdio.h> #include <string.h> #include <windock2.h> #define BUF_SIZE 100 void CompressSockets(SOCKET hSockArr[], int idx, int total); void CompressEvents(WSAEVENT hEventArr[], int ind, int total); void error_handling(char *message); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET serv_sock; SOCKET clnt_sock; SOCKADDR_IN serv_addr; SOCKADDR_IN clnt_addr; int addr_size; SOCKET hSockArr[WSA_MAXIMUM_WAIT_EVENTS]; WSAEVENT hEventArr[WSA_MAXIMUM_WAIT_EVENTS]; WSAEVENT newEvent; WSANETWORKEVENTS netEvents; int numOfClntSock=0; int strLen, i; int posInfo, startIdx; int clntAdrLen; char msg[BUF_SIZE]; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0) error_handling("WSAStartup() error!"); serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock==INVALID_SOCKET) error_handling("socket() error"); addr_size = sizeof(SOCKADDR_IN); memset(&serv_addr, 0, addr_size); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); if(bind(serv_sock, (SOCKADDR*)&serv_addr, addr_size)==SOCKET_ERROR) error_handling("bind() error"); if(listen(serv_sock, 5)==SOCKET_ERROR) error_handling("listen() error"); newEvent = WSACreateEvent(); if(WSAEventSelect(serv_sock, newEvent, FD_ACCEPT)==SOCKET_ERROR) error_handling("WSAEventSelect() error"); hSockArr[numOfClntSock] = serv_sock; hEventArr[numOfClntSock] = newEvent; numOfClntSock++; while(1) { posInfo = WSAWaitForMultipleEvents(numOfClntSock, hEventArr, FALSE, WSA_INFINITE, FALSE); startIdx = posInfo-WSA_WAIT_EVENT_0; for(i=startIdx; i<numOfClntSock; i++) { int sigEventIdx = WSAWaitForMultipleEvents(1, &hEventArr[i], TRUE, 0, FALSE); if(sigEventIdx==WSA_WAIT_FAILED || sigEventIdx==WSA_WAIT_TIMEOUT) { continue; } else { sigEventIdx = i; WASEnumNetworkEvents(hSockArr[sigEventIdx], hEventArr[sigEventIdx], &netEvents); if(netEvents.lNetworkEvents & FD_ACCCEPT) { if(netEvents.iErrorCode[FD_ACCEPT_BIT]!=0) { puts("ACCEPT Error"); break; } clnt_sock = accept(hSockArr[sigEventIdx], (SOCKADDR*)&clnt_addr, &addr_size); newEvent = WSACreateEvent(); WSAEventSelect(clnt_sock, newEvent, FD_READ|FD_CLOSE); hSockArr[numOfClntSock] = clnt_sock; hEventArr[numOfClntSock] = newEvent; numOfClntSock++; puts("connected new client..."); } if(netEvents.lNetworkEvents & FD_READ) { if(netEvents.iErrorCode[FD_READ_BIT]!=0) { puts("READ Error"); break; } strLen = recv(hSockArr[sigEventIdx], msg, sizeof(msg), 0); send(hSockArr[sigEventIdx], msg, strLen, 0); } if(netEvents.lNetworkEvents & FD_CLOSE) { if(netEvents.iErrorCode[FD_CLOSE_BIT]!=0) { puts("CLOSE Error"); break; } WSACloseEvent(hEventArr[sigEventIdx]); closesocket(hSockArr[sigEventIdx]); numOfClntSock--; CompressSockets(hSockArr, sigEventIdx, numOfClntSock); CompressEvents(hEventArr, sigEventIdx, numOfClntSock); } } } } WSACleanup(); return 0; } void CompressSockets(SOCKET hSockArr[], int idx, int total) { int i; for(i=idx; i<total; i++) hSockArr[i] = hSockArr[i+1]; } void CompressEvents(WSAEVENT hEventArr[], int idx, int total) { int i; for(i=idx; i<total; i++) hEventArr[i] = hEventArr[i+1]; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
cl /EHsc AsyncNotiEchoServ_win.c /Fe:AsyncNotiEchoServ_win.exe AsyncNotiEchoServ_win 9190