同步的关键是函数的调用及返回时刻,以及数据传输的开始和完成时刻。
异步(Asynchronous),不一致。
异步I/O指I/O函数的返回时刻与数据收发的完成时刻不一致。
同步I/O的过程中函数无法返回,不能执行其他任务。
异步I/O立即返回函数,可以执行其他任务,更有效地使用CPU。
异步工作方式“通知I/O”。
通知I/O:通知输入缓冲收到数据并需要读取,以及输出缓冲为空故可以发送数据。
select是同步方式通知I/O,需要I/O或可以进行I/O的时间点(I/O相关事件发生的时间点)与select函数的返回时间点一致。
异步通知I/O中,指定I/O监视对象的函数和实际验证状态变化的函数是相互分离的。指定监视对象后可以离开执行其他任务,最后再回来验证状态变化。
WSAAsyncSelect函数,需指定Windows句柄以获取发生的事件(UI相关内容),本书不会涉及。
I/O的状态变化
#include <winsock2.h> //成功0,失败SOCKET_ERROR int WSAEventSelect( SOCKET s, WSAEVENT hEventObject, //事件对象句柄 long lNetworkEvents //监视的事件类型信息,或运算 );
传入参数s的套接字内只要发生lNetworkEvents中指定事件之一, WSAEventSelect函数就将hEventObject句柄所指内核对象改为signaled状态。该函数是连接事件对象和套接字的函数,以异步通知方式工作,调用后直接返回。
事件类型信息:
CreateEvent函数可以创建auto-reset或manual-reset模式事件对象。
WSACreateEvent只创建manual-reset模式non-signaled状态的事件对象。
#include <winsock2.h> //#define WSAEVENT HANDLE //失败WSA_INVALID_EVENT WSAEVENT WSACreateEvent(void); //销毁上述函数创建的事件对象 BOOL WSACloseEvent(WSAEVENT hEvent);
#include <winsock2.h> //失败WSA_INVALID_EVENT DWORD WSAWaitForMultipleEvents( DWORD cEvents,//验证是否转为signaled状态的事件对象个数 const WSAEVENT *lphEvents,//事件对象句柄数组 BOOL fWaitAll,//TRUE,所有事件对象signaled状态才返回;FALSE,任一事件对象signaled状态就返回 DWORD dwTimeout,//超时(1/1000秒),WSA_INFINITE一直等待 BOOL fAlertable//TRUE时进入alertable wait(可警告等待)状态 ); //返回值减去WSA_WAIT_EVENT_0,得到转为signaled状态事件对象句柄对应索引(事件对象句柄数组)。 //多个signaled状态事件对象,得到较小值(调用一次只能获取一个signaled状态事件对象句柄)。 //超时返回WAIT_TIMEOUT
最多可传递64个事件对象,如需监视更多句柄,只能创建线程或扩展保存句柄的数组,并多次调用上述函数。
获取所有signaled状态的事件对象。
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; }
#include <winsock2.h> //成功0,失败SOCKET_ERROR int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, //与套接字关联的signaled状态的事件对象句柄 LPWSANETWORKEVENTS lpNetworkEvents//保存发生的事件类型信息和错误信息 );
上述函数将manual-reset模式事件对象改为non-signaled状态,无需单独调用ResetEvent函数。
type struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS]; } WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
查看发生的事件类型
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) { // }
iErrorCode数组保存错误信息。
WSANETWORKEVENTS netEvents; WSAEnumNetworkEvents(hSock, hEvent, &netEvents); if(netEvents.iErrorCode[FD_READ_BIT] != 0) { // }
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