EventSelect套接字链表对象和线程链表对象组合下的事件Select模型

简介: WSAEventSelect模型的是事件句柄数组和套节字句柄数组的方式去实现事件Select模型的 接下来用的是套接字链表对象和线程链表对象组合下的事件Select模型   ///////////////////////////////////////////////////// // EventSelect.

WSAEventSelect模型的是事件句柄数组和套节字句柄数组的方式去实现事件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_EVENTS PSOCKET_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; else pThread->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); } } else break; } else if(event.lNetworkEvents & FD_CLOSE) // 套节字关闭 { break; } else if(event.lNetworkEvents & FD_WRITE) // 套节字可写 { if(event.iErrorCode[FD_WRITE_BIT] == 0) { } else break; } 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); } else printf(" 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.dll WSADATA 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; } //Client int 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; }

目录
相关文章
|
2月前
|
Java
网络 I/O:单 Selector 多线程(单线程模型)
网络 I/O:单 Selector 多线程(单线程模型)
|
3月前
|
存储 设计模式 安全
Java 编程问题:十、并发-线程池、可调用对象和同步器2
Java 编程问题:十、并发-线程池、可调用对象和同步器
32 0
|
3月前
|
缓存 安全 Java
Java 编程问题:十、并发-线程池、可调用对象和同步器1
Java 编程问题:十、并发-线程池、可调用对象和同步器
43 0
|
1月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
3月前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
42 1
|
25天前
|
存储 开发框架 安全
【C++ 线程】深入理解C++线程管理:从对象生命周期到线程安全
【C++ 线程】深入理解C++线程管理:从对象生命周期到线程安全
86 0
|
1月前
|
存储 安全 Java
调用链跨线程传递 ThreadLocal 对象对比
说起本地线程专属变量,大家首先会想到的是 JDK 默认提供的 ThreadLocal,用来存储在整个链路中都需要访问的数据,并且是线程安全的。由于在落地全链路压测的过程中,一个基本并核心的功能需求是流量标记需要在整个链路中进行传递,那么线程上下文环境成为解决这个问题最合适的技术。
38 2
调用链跨线程传递 ThreadLocal 对象对比
|
2月前
|
消息中间件 安全 Java
多线程(初阶七:阻塞队列和生产者消费者模型)
多线程(初阶七:阻塞队列和生产者消费者模型)
30 0
|
3月前
|
缓存 NoSQL 安全
Redis 新特性篇:多线程模型解读
Redis 新特性篇:多线程模型解读
47 5
|
3月前
|
存储 JSON 运维
【运维】Powershell 服务器系统管理信息总结(进程、线程、磁盘、内存、网络、CPU、持续运行时间、系统账户、日志事件)
【运维】Powershell 服务器系统管理信息总结(进程、线程、磁盘、内存、网络、CPU、持续运行时间、系统账户、日志事件)
49 0