用事件通知方式实现的重叠I/O模型

来源:互联网 发布:mysql 数据表日志 编辑:程序博客网 时间:2024/05/22 15:27
//服务端源码//用事件通知方式实现的重叠I/O模型#include <winsock2.h>#include <stdio.h>#define PORT 5566#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")typedef struct{  WSAOVERLAPPED overlap;  WSABUF Buffer;  char szMessage[MSGSIZE];  DWORD NumberOfBytesRecvd;  DWORD Flags;}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;int g_iTotalConn = 0;SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS];DWORD WINAPI WorkerThread(LPVOID);void Cleanup(int);int main(){WSADATA wsaData;WORD wVersionRequested;int err;wVersionRequested = MAKEWORD( 1, 1);err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) {                              return 0;}                                      if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){WSACleanup( );return 0; }/*WSAStartup(0x0202, &wsaData);*/ // 初始化windows socket库 SOCKET sListen, sClient; SOCKADDR_IN local, client; DWORD dwThreadId; int iaddrSize = sizeof(SOCKADDR_IN); // 创建监听socket sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //默认设置 WSA_FLAG_OVERLAPPED // 绑定 local.sin_addr.S_un.S_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(PORT); bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN)); // 监听 listen(sListen, 3); // 创建工作者线程 CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId); while (TRUE) {   // 接受连接   sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);   printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));   g_CliSocketArr[g_iTotalConn] = sClient;   // 分配一个单io操作数据结构   g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc(   GetProcessHeap(),   HEAP_ZERO_MEMORY,   sizeof(PER_IO_OPERATION_DATA));   //This function allocates a block of memory from a heap. The allocated memory is not movable.    g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;   g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->szMessage;   g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();   g_pPerIODataArr[g_iTotalConn]->Flags=0;   // 开始一个异步操作   WSARecv(       g_CliSocketArr[g_iTotalConn],       &g_pPerIODataArr[g_iTotalConn]->Buffer,       1,       &g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd,       &g_pPerIODataArr[g_iTotalConn]->Flags,       &g_pPerIODataArr[g_iTotalConn]->overlap,       NULL);   //WSARecv的参数中都有一个 Overlapped 参数,我们可以假设是把我们的WSARecv这样的操作"绑定"到这个重叠结构上,   //提交一个请求,而不是将操作立即完成,其他的事情就交给重叠结构去做,而其中重叠结构又要与Windows的事件对象"绑定"在一起,   // 这样我们调用完 WSARecv 以后就可以"坐享其成",等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,  //然后我们就可以来根据重叠操作的结果取得我们想要的数据了。       /*int WSARecv(   SOCKET s, // 当然是投递这个操作的套接字   LPWSABUF lpBuffers, // 接收缓冲区,与Recv函数不同   // 这里需要一个由WSABUF结构构成的数组   DWORD dwBufferCount, // 数组中WSABUF结构的数量   LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数   LPDWORD lpFlags, // 一个指向标志位的指针   LPWSAOVERLAPPED lpOverlapped, // “绑定”的重叠结构   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为 NULL);无错返回0,否则返回-1(SOCKET_ERROR )*/   g_iTotalConn++;} closesocket(sListen); WSACleanup(); return 0;}DWORD WINAPI WorkerThread(LPVOID lpParam){ int ret, index; DWORD cbTransferred; while (TRUE) { //判断出一个重叠 I/O 调用是否完成   ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);   if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)   {     continue;   }       /*DWORD WSAWaitForMultipleEvents(   DWORD cEvents, // 等候事件的总数量   const WSAEVENT* lphEvents, // 事件数组的指针   BOOL fWaitAll, // 这个要多说两句:   // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回   // FALSE则任何一个事件被传信函数都要返回   // 我们这里肯定是要设置为FALSE的   DWORD dwTimeout, // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT   // 如果设置为0,函数会立即返回   // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回,   //则WSAWaitForMultipleEvents 永远等待,不会出现超时现象。   BOOL fAlertable   //该值指定线程是否为alertable等待状态,此时系统能执行一些I/O 完成例程。如果值为真,   当系统执行I/O //完成例程时线程被处于altertable 等待状态且WSAWaitForMultipleEvents    返回。在这种情况下,返回   //WSA_WAIT_IO_COMPLETION ,并且等待的event 不会触发信号状态。程序必须重新调用   //WSAWaitForMultipleEvents 函数。如果为false,线程不处于altertable 等待状态,   //并且I/O 完成例程不会执行。    );        返回值:   WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait   WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效   如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个   索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。       注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象   定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,   如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一   个线程池If the WSAWaitForMultipleEvents function succeeds, the return value indicates the event object that caused the function to return.*/ index = ret - WSA_WAIT_EVENT_0; WSAResetEvent(g_CliEventArr[index]);//手动设置为未传信 //http://baike.baidu.com/view/567662.htm BOOL bRet=WSAGetOverlappedResult(g_CliSocketArr[index], &g_pPerIODataArr[index]->overlap, &cbTransferred, TRUE, &g_pPerIODataArr[g_iTotalConn]->Flags);//判断该重叠调用到底是成功,还是失败/*if (!bRet) {int error=WSAGetLastError();continue; } else {*/ if(cbTransferred==0) { Cleanup(index); } else { // g_pPerIODataArr[index]->szMessage保存了接受到的数据 g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0'; printf("%s\n",g_pPerIODataArr[index]->szMessage); // 进行另一个异步操作 WSARecv( g_CliSocketArr[index], &g_pPerIODataArr[index]->Buffer, 1, &g_pPerIODataArr[index]->NumberOfBytesRecvd, &g_pPerIODataArr[index]->Flags, &g_pPerIODataArr[index]->overlap, NULL);} /*}*/} return 0;}void Cleanup(int index){  closesocket(g_CliSocketArr[index]);  WSACloseEvent(g_CliEventArr[index]);  HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[index]);  if (index < g_iTotalConn - 1)  {      g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];    g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];    g_pPerIODataArr[index] = g_pPerIODataArr[g_iTotalConn - 1];  }  g_pPerIODataArr[--g_iTotalConn] = NULL;}

原创粉丝点击