EventSelect套接字链表对象和线程链表对象组合下的事件Select模型时间:2022-07-08 00:16:42WSAEventSelect模型的是事件句柄数组和套节字句柄数组的方式去实现事件Select模型的 接下来用的是套接字链表对象和线程链表对象组合下的事件Select模型 /////////////////////////////////////////////////////// EventSelect.h文件DWORD WINAPI ServerThread(LPVOID lpParam);// 套节字对象typedef struct _SOCKET_OBJ{SOCKET s;// 套节字句柄HANDLE event;// 与此套节字相关联的事件对象句柄sockaddr_in addrRemote;// 客户端地址信息_SOCKET_OBJ *pNext;// 指向下一个SOCKET_OBJ对象,为的是连成一个表} SOCKET_OBJ, *PSOCKET_OBJ;// 线程对象typedef struct _THREAD_OBJ{HANDLE events[WSA_MAXIMUM_WAIT_EVENTS];// 记录当前线程要等待的事件对象的句柄int nSocketCount;// 记录当前线程处理的套节字的数量 <= WSA_MAXIMUM_WAIT_EVENTSPSOCKET_OBJ pSockHeader;// 当前线程处理的套节字对象列表,pSockHeader指向表头PSOCKET_OBJ pSockTail;// pSockTail指向表尾CRITICAL_SECTION cs;// 关键代码段变量,为的是同步对本结构的访问_THREAD_OBJ *pNext;// 指向下一个THREAD_OBJ对象,为的是连成一个表} THREAD_OBJ, *PTHREAD_OBJ;// 线程列表PTHREAD_OBJ g_pThreadList;// 指向线程对象列表表头CRITICAL_SECTION g_cs;// 同步对此全局变量的访问// 状态信息LONG g_nTatolConnections;// 总共连接数量LONG g_nCurrentConnections;// 当前连接数量// 申请一个套节字对象,初始化它的成员PSOCKET_OBJ GetSocketObj(SOCKET s){PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));if(pSocket != NULL){pSocket->s = s;pSocket->event = ::WSACreateEvent();}return pSocket;}// 释放一个套节字对象void FreeSocketObj(PSOCKET_OBJ pSocket){::CloseHandle(pSocket->event);if(pSocket->s != INVALID_SOCKET){::closesocket(pSocket->s);}::GlobalFree(pSocket);}// 申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中PTHREAD_OBJ GetThreadObj(){PTHREAD_OBJ pThread = (PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));if(pThread != NULL){::InitializeCriticalSection(&pThread->cs);// 创建一个事件对象,用于指示该线程的句柄数组需要重组pThread->events[0] = ::WSACreateEvent();// 将新申请的线程对象添加到列表中::EnterCriticalSection(&g_cs);pThread->pNext = g_pThreadList;g_pThreadList = pThread;::LeaveCriticalSection(&g_cs);}return pThread;}// 释放一个线程对象,并将它从线程对象列表中移除void FreeThreadObj(PTHREAD_OBJ pThread){// 在线程对象列表中查找pThread所指的对象,如果找到就从中移除::EnterCriticalSection(&g_cs);PTHREAD_OBJ p = g_pThreadList;if(p == pThread)// 是第一个?{g_pThreadList = p->pNext;}else{while(p != NULL && p->pNext != pThread){p = p->pNext;}if(p != NULL){// 此时,p是pThread的前一个,即“p->pNext == pThread”p->pNext = pThread->pNext;}}::LeaveCriticalSection(&g_cs);// 释放资源::CloseHandle(pThread->events[0]);::DeleteCriticalSection(&pThread->cs);::GlobalFree(pThread);}// 重新建立线程对象的events数组void RebuildArray(PTHREAD_OBJ pThread){::EnterCriticalSection(&pThread->cs);PSOCKET_OBJ pSocket = pThread->pSockHeader;int n = 1;// 从第1个开始写,第0个用于指示需要重建了while(pSocket != NULL){pThread->events[n++] = pSocket->event;pSocket = pSocket->pNext;}::LeaveCriticalSection(&pThread->cs);}/////////////////////////////////////////////////////////////////////// 向一个线程的套节字列表中插入一个套节字BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket){BOOL bRet = FALSE;::EnterCriticalSection(&pThread->cs);if(pThread->nSocketCount < WSA_MAXIMUM_WAIT_EVENTS - 1){if(pThread->pSockHeader == NULL){pThread->pSockHeader = pThread->pSockTail = pSocket;}else{pThread->pSockTail->pNext = pSocket;pThread->pSockTail = pSocket;}pThread->nSocketCount ++;bRet = TRUE;}::LeaveCriticalSection(&pThread->cs);// 插入成功,说明成功处理了客户的连接请求if(bRet){::InterlockedIncrement(&g_nTatolConnections);::InterlockedIncrement(&g_nCurrentConnections);}return bRet;}// 将一个套节字对象安排给空闲的线程处理void AssignToFreeThread(PSOCKET_OBJ pSocket){pSocket->pNext = NULL;::EnterCriticalSection(&g_cs);PTHREAD_OBJ pThread = g_pThreadList;// 试图插入到现存线程while(pThread != NULL){if(InsertSocketObj(pThread, pSocket))break;pThread = pThread->pNext;}// 没有空闲线程,为这个套节字创建新的线程if(pThread == NULL){pThread = GetThreadObj();InsertSocketObj(pThread, pSocket);::CreateThread(NULL, 0, ServerThread, pThread, 0, NULL);}::LeaveCriticalSection(&g_cs);// 指示线程重建句柄数组::WSASetEvent(pThread->events[0]);}// 从给定线程的套节字对象列表中移除一个套节字对象void RemoveSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket){::EnterCriticalSection(&pThread->cs);// 在套节字对象列表中查找指定的套节字对象,找到后将之移除PSOCKET_OBJ pTest = pThread->pSockHeader;if(pTest == pSocket){if(pThread->pSockHeader == pThread->pSockTail)pThread->pSockTail = pThread->pSockHeader = pTest->pNext;elsepThread->pSockHeader = pTest->pNext;}else{while(pTest != NULL && pTest->pNext != pSocket)pTest = pTest->pNext;if(pTest != NULL){if(pThread->pSockTail == pSocket)pThread->pSockTail = pTest;pTest->pNext = pSocket->pNext;}}pThread->nSocketCount --;::LeaveCriticalSection(&pThread->cs);// 指示线程重建句柄数组::WSASetEvent(pThread->events[0]);// 说明一个连接中断::InterlockedDecrement(&g_nCurrentConnections);}BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket){// 获取具体发生的网络事件WSANETWORKEVENTS event;::WSAEnumNetworkEvents(pSocket->s, pSocket->event, &event);do{if(event.lNetworkEvents & FD_READ)// 套节字可读{if(event.iErrorCode[FD_READ_BIT] == 0){char szText[256];int nRecv = ::recv(pSocket->s, szText, strlen(szText), 0);if(nRecv > 0){szText[nRecv] = '/0';printf("接收到数据:%s /n", szText);}}elsebreak;}else if(event.lNetworkEvents & FD_CLOSE)// 套节字关闭{break;}else if(event.lNetworkEvents & FD_WRITE)// 套节字可写{if(event.iErrorCode[FD_WRITE_BIT] == 0){}elsebreak;}return TRUE;}while(FALSE);// 套节字关闭,或者有错误发生,程序都会转到这里来执行RemoveSocketObj(pThread, pSocket);FreeSocketObj(pSocket);return FALSE;}PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) // nIndex从1开始{// 在套节字列表中查找PSOCKET_OBJ pSocket = pThread->pSockHeader;while(--nIndex){if(pSocket == NULL)return NULL;pSocket = pSocket->pNext;}return pSocket;}DWORD WINAPI ServerThread(LPVOID lpParam){// 取得本线程对象的指针PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;while(TRUE){//等待网络事件int nIndex = ::WSAWaitForMultipleEvents(pThread->nSocketCount + 1, pThread->events, FALSE, WSA_INFINITE, FALSE);nIndex = nIndex - WSA_WAIT_EVENT_0;// 查看受信的事件对象for(int i=nIndex; i<pThread->nSocketCount + 1; i++){nIndex = ::WSAWaitForMultipleEvents(1, &pThread->events[i], TRUE, 1000, FALSE);if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT){continue;}else{if(i == 0)// events[0]受信,重建数组{RebuildArray(pThread);// 如果没有客户I/O要处理了,则本线程退出if(pThread->nSocketCount == 0){FreeThreadObj(pThread);return 0;}::WSAResetEvent(pThread->events[0]);}else// 处理网络事件{// 查找对应的套节字对象指针,调用HandleIO处理网络事件PSOCKET_OBJ pSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);if(pSocket != NULL){if(!HandleIO(pThread, pSocket))RebuildArray(pThread);}elseprintf(" Unable to find socket object /n ");}}}}return 0;} ///////////////////////////////////////////// EventSelectServer.cpp文件#include <winsock2.h>#pragma comment(lib, "WS2_32")// 链接到WS2_32.lib#include <stdio.h>#include <windows.h>#include "EventSelect.h"class CInitSock{public:CInitSock(BYTE minorVer = 2, BYTE majorVer = 2){// 初始化WS2_32.dllWSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if(::WSAStartup(sockVersion, &wsaData) != 0){exit(0);}}~CInitSock(){::WSACleanup();}};// 初始化Winsock库CInitSock theSock;int main1();int main2();void main(){printf("Please select a item:/n");printf("s:Server/n");printf("c:Client/n");char a;while(1){scanf("%c",&a);if(a=='s'){main1();break;}else if(a=='c'){main2();break;}else continue;}}int main1(){USHORT 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_un.S_addr = INADDR_ANY;if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR){printf(" Failed bind() /n");return -1;}::listen(sListen, 200);// 创建事件对象,并关联到监听的套节字WSAEVENT event = ::WSACreateEvent();::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);::InitializeCriticalSection(&g_cs);// 处理客户连接请求,打印状态信息while(TRUE){int nRet = ::WaitForSingleObject(event, 5*1000);if(nRet == WAIT_FAILED){printf(" Failed WaitForSingleObject() /n");break;}else if(nRet == WSA_WAIT_TIMEOUT)// 定时显式状态信息{printf(" /n");printf(" TatolConnections: %d /n", g_nTatolConnections);printf(" CurrentConnections: %d /n", g_nCurrentConnections);continue;}else// 有新的连接未决{::ResetEvent(event);// 循环处理所有未决的连接请求while(TRUE){sockaddr_in si;int nLen = sizeof(si);SOCKET sNew = ::accept(sListen, (sockaddr*)&si, &nLen);if(sNew == SOCKET_ERROR)break;PSOCKET_OBJ pSocket = GetSocketObj(sNew);pSocket->addrRemote = si;::WSAEventSelect(pSocket->s, pSocket->event, FD_READ|FD_CLOSE|FD_WRITE);AssignToFreeThread(pSocket);}}}::DeleteCriticalSection(&g_cs);return 0;}//Clientint main2(){// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(s == INVALID_SOCKET){printf(" Failed socket() /n");return 0;}// 也可以在这里调用bind函数绑定一个本地地址// 否则系统将会自动安排// 填写远程地址信息sockaddr_in servAddr; servAddr.sin_family = AF_INET;servAddr.sin_port = htons(4567);// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址// 如果你的计算机没有联网,直接使用127.0.0.1即可servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1){printf(" Failed connect() /n");return 0;} char szText[256];while(TRUE){scanf("%s",szText);//puts(szText);// 向服务器端发送数据::send(s, szText, strlen(szText), 0);}// 关闭套节字::closesocket(s);return 0;}