重叠I/O之事件通知
2016-11-28 13:16
477 查看
使用重叠I/O之事件通知模型步骤如下:
一、打开服务器(包括初始化、创建socket、绑定、监听)
其中创建socket的时候可以使用WSASocket,也可以简单的使用socket,因为socket默认已经加上了WSA_FLAG_OVERLAPPED这个标志。
二、创建ThreadAccept线程
这个线程主要是用来不断的接受client的请求,并为每一个成功连接到server的client创建一个事件对象并将它与重叠结构绑定起来。然后以此为参数调用WSARecv函数,也就是投递一个事件,剩下的事情交给操作系统去做,当有对应的事件发生,系统就会以事件的形式通知应用程序。
伪代码如下:
其中一些参数的初始化和错误处理省略了。
如图:
三、创建ThreadHandle线程
这个线程主要用来等待事件的发生以及处理接收到的数据。
其主要步骤就是等待某个socket上的事件发生,然后触发这个函数返回一个index值(注意,这里的index还不是对应事件数组的索引,还要减去WSA_WAIT_EVENT_0),之后通过这个index索引到对应的socket,以此为参数调用WSAGetOverlappedResult函数得到重叠操作的结果。在之后就是处理接收到的数据以及再一次投递一个WSARecv。
伪代码如下:
其中一些参数的初始化和错误处理省略了。
如图:
示例代码:
main function
OpenServer function
ThreadAccept function
ThreadHandle function
一、打开服务器(包括初始化、创建socket、绑定、监听)
其中创建socket的时候可以使用WSASocket,也可以简单的使用socket,因为socket默认已经加上了WSA_FLAG_OVERLAPPED这个标志。
二、创建ThreadAccept线程
这个线程主要是用来不断的接受client的请求,并为每一个成功连接到server的client创建一个事件对象并将它与重叠结构绑定起来。然后以此为参数调用WSARecv函数,也就是投递一个事件,剩下的事情交给操作系统去做,当有对应的事件发生,系统就会以事件的形式通知应用程序。
伪代码如下:
while(1) { accept a new client; //accept() ... Create a new event object; //WSACreateEvent() ... bind with overlapped struct; //WSAOVERLAPPED ... post a WSARecv; //WSARecv() }
其中一些参数的初始化和错误处理省略了。
如图:
三、创建ThreadHandle线程
这个线程主要用来等待事件的发生以及处理接收到的数据。
其主要步骤就是等待某个socket上的事件发生,然后触发这个函数返回一个index值(注意,这里的index还不是对应事件数组的索引,还要减去WSA_WAIT_EVENT_0),之后通过这个index索引到对应的socket,以此为参数调用WSAGetOverlappedResult函数得到重叠操作的结果。在之后就是处理接收到的数据以及再一次投递一个WSARecv。
伪代码如下:
while(1) { wait for event happend; //WSAWaitForMultipleEvents() ... reset the event object; //WSAResetEvent() ... get the overlapped operation result;//WSAGetOverlappedResult() ... handle the recv data; ... post a WSASend or WSARecv; }
其中一些参数的初始化和错误处理省略了。
如图:
示例代码:
main function
#include <WinSock2.h> #include <process.h> #pragma comment(lib,"ws2_32.lib") #define MAXBUF 128 //缓冲区大小 SOCKET g_sServer = INVALID_SOCKET; //server socket SOCKET g_sClient[WSA_MAXIMUM_WAIT_EVENTS] = { INVALID_SOCKET }; //client socket array WSAEVENT g_event[WSA_MAXIMUM_WAIT_EVENTS]; //event array WSAOVERLAPPED g_wsaOverLapped_Recv[WSA_MAXIMUM_WAIT_EVENTS]; //接收重叠结构数组 WSAOVERLAPPED g_wsaOverLapped_Send[WSA_MAXIMUM_WAIT_EVENTS]; //发送重叠结构数组 WSABUF g_wsaBuf[WSA_MAXIMUM_WAIT_EVENTS] = { 0 }; //接收和发送缓冲区,因为是转发,所以接收和发送的数据是一样的 DWORD g_dwRecvBytes[WSA_MAXIMUM_WAIT_EVENTS] = {0}; //接收到的字节数 DWORD g_dwSendBytes[WSA_MAXIMUM_WAIT_EVENTS] = { 0 }; //接收到的字节数 int g_numClient = 0; //当前连接的client数 int main() { //打开服务器 if (OpenTCPServer()) { _beginthreadex(NULL, 0, ThreadAccept, NULL, 0, NULL); //开启接受client请求线程 _beginthreadex(NULL, 0, ThreadHandleData, NULL, 0, NULL); //开启处理数据线程 } Sleep(100000000); //主函数睡眠 closesocket(g_sServer); WSACleanup(); return 0; }
OpenServer function
/* @function OpenTCPServer 打开TCP服务器 @return 成功返回TRUE 失败返回FALSE */ BOOL OpenTCPServer() { WSADATA wsaData = { 0 }; SOCKADDR_IN ServerAddr = { 0 }; BOOL bRet = FALSE; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(18000); ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); do { if (!WSAStartup(MAKEWORD(2, 2), &wsaData)) { if (LOBYTE(wsaData.wVersion) == 2 || HIBYTE(wsaData.wVersion) == 2) { //在套接字上使用重叠I/O模型,必须使用WSA_FLAG_OVERLAPPED标志创建套接字 //g_sServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); g_sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (g_sServer != INVALID_SOCKET) { if (SOCKET_ERROR != bind(g_sServer, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr))) { if (SOCKET_ERROR != listen(g_sServer, SOMAXCONN)) { bRet = TRUE; break; } closesocket(g_sServer); } closesocket(g_sServer); } } } } while (FALSE); return bRet; }
ThreadAccept function
//接受client请求线程 unsigned int __stdcall ThreadAccept(void* lparam) { SOCKADDR_IN addrClient = { 0 }; int iLenAddr = sizeof(addrClient); //初始化接收重叠结构 memset(&g_wsaOverLapped_Recv, 0, sizeof(g_wsaOverLapped_Recv)); //初始化发送重叠结构 memset(&g_wsaOverLapped_Send, 0, sizeof(g_wsaOverLapped_Send)); while (g_numClient < WSA_MAXIMUM_WAIT_EVENTS) //当g_numClient等于最大连接数的时候,退出循环 { //接受新的请求 g_sClient[g_numClient] = accept(g_sServer, (SOCKADDR*)&addrClient, &iLenAddr); if(g_sClient[g_numClient] == INVALID_SOCKET) continue; //创建事件对象 g_event[g_numClient] = WSACreateEvent(); //为该结构的hEvent字段赋值为刚刚创建的事件对象 g_wsaOverLapped_Recv[g_numClient].hEvent = g_event[g_numClient]; //为该结构的hEvent字段赋值为刚刚创建的事件对象 g_wsaOverLapped_Send[g_numClient].hEvent = g_event[g_numClient]; //为每个client的buf分配空间 g_wsaBuf[g_numClient].buf = new char[MAXBUF]; g_wsaBuf[g_numClient].len = MAXBUF; memset(g_wsaBuf[g_numClient].buf, 0, MAXBUF); //这个标志一定要放进去,不然会出错(可能是如果没有传一个指针进去,函数内部引用了一个空指针而出错) DWORD dwFlag = 0; //投递一个recv事件,将该client的recv事件与接收重叠结构绑定起来,接下来的事就丢给重叠结构去完成,等待完成后的消息即可 if (SOCKET_ERROR == WSARecv(g_sClient[g_numClient], &g_wsaBuf[g_numClient], 1, &g_dwRecvBytes[g_numClient], &dwFlag, &g_wsaOverLapped_Recv[g_numClient], NULL)) { //返回WSA_IO_PENDING是正常的,说明正在进行I/O操作 if (WSAGetLastError() != WSA_IO_PENDING) return 0; } printf("accept connection from IP: %s,PORT: %d\n", inet_ntoa(addrClient.sin_addr), htons(addrClient.sin_port)); g_numClient++; } return 0; }
ThreadHandle function
//处理数据线程 unsigned int __stdcall ThreadHandleData(void* lparam) { DWORD dwFlags = 0; //这个标志一定要放进去,不然会出错 DWORD dwBytesRecv; //接收的字节数 DWORD dwBytesSend; //发送的字节数 while (1) { //无client的时候不进行任何处理 if (g_numClient < 1) { Sleep(100); continue; } dwBytesRecv = 0; dwBytesSend = 0; //等待事件 DWORD dwIndex = WSAWaitForMultipleEvents(g_numClient, g_event, FALSE, 1000, FALSE); if (dwIndex == WSA_WAIT_FAILED) { printf("WSAWaitForMultipleEvents failed with error code:%d\n", WSAGetLastError()); return 1; } else if (dwIndex == WSA_WAIT_TIMEOUT) { Sleep(100); continue; } // dwIndex -= WSA_WAIT_EVENT_0; //重置事件 WSAResetEvent(g_event[dwIndex]); //获取接收重叠结构操作后的结果 BOOL bRet = WSAGetOverlappedResult(g_sClient[dwIndex],&g_wsaOverLapped_Recv[dwIndex], &dwBytesRecv, FALSE, &dwFlags); if (dwBytesRecv == 0) //如果传输的数据为0,则表示client已经断开连接 { printf("%d disconnect\n", dwIndex); continue; } else if (bRet == FALSE) //有三种原因:第一,重叠操作还未完成。第二,重叠操作完成,但存在错误 { //第三,由于该函数的一个或者多个参数错误,而导致不能确定重叠操作完成的状态 int i = WSAGetLastError(); continue; } else //如果成功返回,则进行数据的处理,然后再投递一次recv事件 { g_wsaBuf[dwIndex].len = strlen(g_wsaBuf[dwIndex].buf); if (g_wsaBuf[dwIndex].len != 0) //printf("Recv%d:%s\n", dwIndex,g_wsaBuf[dwIndex].buf); //投递send事件 if (SOCKET_ERROR == WSASend(g_sClient[dwIndex], &g_wsaBuf[dwIndex], 1, &g_dwSendBytes[dwIndex], dwFlags, &g_wsaOverLapped_Send[dwIndex], NULL)) { if (WSAGetLastError() != WSA_IO_PENDING) return 1; } //投递recv事件 g_wsaBuf[dwIndex].len = MAXBUF; if (SOCKET_ERROR == WSARecv(g_sClient[dwIndex], &g_wsaBuf[dwIndex], 1, &g_dwRecvBytes[dwIndex], &dwFlags, &g_wsaOverLapped_Recv[dwIndex], NULL)) { if (WSAGetLastError() != WSA_IO_PENDING) return 1; } } } return 0; }
相关文章推荐
- 重叠I/O-事件通知
- Overlapped重叠I/O之事件通知
- 重叠I/O之事件对象通知
- 事件通知方式实现的重叠I/O模型
- 重叠I/O之事件对象通知
- 基于事件通知的重叠I/O网络模型
- Socket I/O模型之重叠I/O(overlapped I/O)--事件通知
- Windows socket之重叠IO:事件通知
- 模型设计与实践---(六)重叠IO,事件通知(Overlap Event)
- Windows socket之重叠IO:事件通知
- Windows socket之重叠IO:事件通知
- 用事件通知方式实现的重叠I/O模型
- 重叠I/O之事件通知
- 重叠IO之事件通知模型
- 重叠I/O之事件通知
- Socket IO重叠模型(事件通知)
- 重叠I/O-事件通知
- 重叠模型--事件对象通知
- winsock IO 模型---重叠IO之事件通知 example code
- Windows Socket I/O模型之 重叠I/O事件通知模式