Iocp

2009-06-04 15:44

//memorypool.h
#pragma once
#include <iostream>
#include <list>
#include <windows.h>

//关键区锁
class CLock
{
     CRITICAL_SECTION _crisection;
public:
     CLock()    
     {
         InitializeCriticalSection( &_crisection );
     }
     ~CLock()  
     {
         DeleteCriticalSection( &_crisection );    
     }
     void lock()
     {
         EnterCriticalSection( &_crisection );      
     }
     void unlock()
     {
         LeaveCriticalSection( &_crisection );      
     }
};
//内存池模型:实质就是一个前向指针链表
template <class particle>
class particle_allocator
{
     enum
     {
         chunk_size =1024
     };
     typedef unsigned char byte;
     struct particle_list
     {
         particle *_data;
         particle_list *_next;
     };
     std::list<particle *> _chunks;
     particle_list* _free_list;

     CLock _guard;
public:
     particle_allocator()
     {
         _free_list = 0;
         _used_particle_number = 0;
         _free_particle_number = 0;
     }
     ~particle_allocator()
     {
         for(std::list<particle *>::iterator iter = _chunks.begin();iter!=_chunks.end();++iter)
         {
             delete (*iter);
         }
         _chunks.clear();
         while(_free_list)
         {
             particle_list *temp = _free_list;
             _free_list = _free_list->_next;
             delete temp;
         }
     }
     particle* alloc_particle()
     {
         _guard.lock();
         byte *momory;
         particle_list* &current_list = _free_list;
         if(!current_list)
         {
             momory = new byte[chunk_size*sizeof(particle)];
             for(int i = 0;i<chunk_size;i++)
             {
                 _chunks.push_front((particle *)(momory + i*chunk_size));
             }
             for(int i =0;i< chunk_size;i++,_free_particle_number++)
             {
                 particle_list *newnode = new particle_list;
                 newnode->_data = (particle *)(momory + i*sizeof(particle));
                 newnode->_next = current_list;
                 current_list = newnode;
             }
         }
         particle   *redata;
         particle_list *de_node = current_list;
         redata = _free_list->_data;
         current_list = current_list->_next;
         ++_used_particle_number;
         --_free_particle_number;
         delete de_node;
         _guard.unlock();
         return new(redata) particle;
     }
     void free_particle(particle   *p)
     {
         particle *free_block = p;
         _guard.lock();
         particle_list* &current_list = _free_list;
         particle_list *newnode = new particle_list;
         newnode->_data = free_block;
         newnode->_next = current_list;
         current_list = newnode;
         --_used_particle_number;
         ++_free_particle_number;
         _guard.unlock();
     }
     void getallocator()
     {
         using namespace std;
         cout << "_used_particle_number is " << _used_particle_number << " _free_particle_number is "<< _free_particle_number << endl;
     }
public:
     size_t _used_particle_number;
     size_t _free_particle_number;
};
//这个是内存分配优化类,由于服务器需要运行在一个相对静态的环境中。所以我第每次分配1024块某种结构的内存。用完继续分配1024块,使用的时候就在分配好的内存里边取一块,用完再归还回去。
//iocpsever.cpp
#include <winsock2.h>
#include "memorypool.h"
#include <Mswsock.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")

// 单句柄数据,每个连接(客户端)对应一个这样的结构。
//只有连接断开,或者服务器关闭的时候才释放
typedef struct tagPER_HANDLE_DATA
{
     SOCKET Socket;
     SOCKADDR_STORAGE ClientAddr;                                                                                                                                                                                                                                                                                                                                                                                                                                                            
     // 将和这个句柄关联的其他有用信息,尽管放在这里面吧
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

// 单I/O操作数据。每次收发数据的时候
//收发数据操作完成数之后释放。
//需要注意的是OVERLAPPED Overlapped一定要放在最前
typedef struct tagPER_IO_DATA
{
     OVERLAPPED Overlapped;
     WSABUF DataBuf;
     char buffer[1024];
     DWORD BufferLen;
     int OperationType;
     SOCKET Socket;
     LPPER_HANDLE_DATA lpSession;
}PER_IO_DATA, *LPPER_IO_DATA;
//操作标志
#define OP_READ     0
#define OP_WRITE     1
#define OP_ACCEPT   2
//连接数据内存池
particle_allocator<PER_IO_DATA> g_per_io_data;
//数据收发内存池
particle_allocator<PER_HANDLE_DATA> g_per_handle_data;
//完成线程
DWORD count = 0;
//保存当前连接数据指针
list<LPPER_HANDLE_DATA> g_session;
//接受事件
HANDLE g_hAcceptExOverEvent = NULL;
//工作线程
DWORD WINAPI ServerWorkerThread(LPVOID lpParam);

using namespace std;

int main(void)
{
     WSADATA wsd;
     SYSTEM_INFO SystemInfo;
     SOCKADDR_IN InternetAddr;
     SOCKET Listen;
     WSAStartup(MAKEWORD(2, 2), &wsd);

     //创建完成端口
     HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_value,
         NULL,
         0,
         0);

     //得到处理器数量
     GetSystemInfo(&SystemInfo);
     //经验公式:一般按公式创建工作线程
     count = 2*(SystemInfo.dwNumberOfProcessors+1);

     for (DWORD i = 0; i < count; ++i)
     {
         HANDLE ThreadHandle;

         ThreadHandle = CreateThread(NULL,
             0,
             ServerWorkerThread,
             CompletionPort,
             0,
             NULL);

         CloseHandle(ThreadHandle);
     }

     //创建监听socket

     Listen = WSASocket(AF_INET,
         SOCK_STREAM,
         0,
         NULL,
         0,
         WSA_FLAG_OVERLAPPED);

     InternetAddr.sin_family = PF_INET;
     InternetAddr.sin_port = htons(10000);
     InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
     //帮定到指定端口

     bind(Listen, (SOCKADDR*)&InternetAddr, sizeof(InternetAddr));

     //开始监听
     listen(Listen, SOMAXCONN);
     //完成端口帮定到监听socket
     if (CreateIoCompletionPort((HANDLE) Listen, CompletionPort, (ULONG_PTR)&Listen, count) == NULL)
     {
         printf("CreateIoCompletionPort failed with error %d/n", GetLastError());
         return 1;
     }
     //创建事件
     g_hAcceptExOverEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
     if(!g_hAcceptExOverEvent)
     {
         return 1;
     }
     //帮定事件,由于我们用AcceptEx是一个异步的投递,这样帮定之后,如果投递的AcceptEx事件全部完成
     //则g_hAcceptExOverEvent事件得到通知,进而同步AcceptEx调用
     if(WSAEventSelect(Listen, g_hAcceptExOverEvent, FD_ACCEPT) == SOCKET_ERROR)
     {
         return 1;
     }
     //由于开始是复位,变成置位
     SetEvent(g_hAcceptExOverEvent);
     BOOL b = TRUE;
    
     while (b)
     {
         //每次投递10次,进入等待状态,当AcceptEx全部完成之后,继续投递
         if(WaitForSingleObject(g_hAcceptExOverEvent, INFINITE) == WAIT_FAILED)
             continue;
         for(int i =0;i<10;i++)
         {
             int zero =0;
             PER_IO_DATA * pper_io_data = NULL;
             DWORD dwAddrLen   = sizeof(sockaddr_in)+16;
             pper_io_data = (PER_IO_DATA *)g_per_io_data.alloc_particle();
             pper_io_data ->Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
             pper_io_data->OperationType = OP_ACCEPT;
             pper_io_data->lpSession=NULL;
             ZeroMemory(&pper_io_data->Overlapped,sizeof(OVERLAPPED));//一定要注意清零
             //这里pper_io_data->Overlapped,将来会在工作线程里返回,工作线程里有这样的代码
             //bRet = GetQueuedCompletionStatus(CompletionPort,
             //     &BytesTransferred,
             //     (PULONG_PTR)  
             //     &lpCompletionKey,
             //     (LPOVERLAPPED*)
             //     &pOldPerIoData,
             //     INFINITE);
             //我们这里为什么可以这么用pOldPerIoData,是因为它是一个PER_IO_DATA指针,但是PER_IO_DATA结构的第一成员是
             //Overlapped,所以pper_io_data->Overlapped与pOldPerIoData地址是相同的。
             if(!AcceptEx(Listen,pper_io_data ->Socket,pper_io_data ->buffer,0,dwAddrLen,dwAddrLen,&pper_io_data ->BufferLen,&pper_io_data->Overlapped))
             {
                 if(WSAGetLastError() != ERROR_IO_PENDING)//对于AcceptEx,WSARecv,WSASend一定要有这样的判断,因为是异步的所以不会立即完成
                 {
                     closesocket(pper_io_data->Socket);
                     g_per_io_data.free_particle(pper_io_data);     //归还结构体到内存池
                     continue;
                 }

             }
         }
     }
     CloseHandle(g_hAcceptExOverEvent);
     return 0;
}
DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{    
     HANDLE CompletionPort = (HANDLE)lpParam;
     DWORD BytesTransferred;
     LPPER_IO_DATA pOldPerIoData = NULL;
     LPPER_IO_DATA pNewPerIoData = NULL;
     LPPER_HANDLE_DATA lpSession =NULL;
     LPVOID lpCompletionKey = NULL;
     DWORD RecvBytes;
     DWORD Flags;
     BOOL bRet = FALSE;

     while (1)
     {
         bRet = GetQueuedCompletionStatus(CompletionPort,
             &BytesTransferred,
             (PULONG_PTR)  
             &lpCompletionKey,
             (LPOVERLAPPED*)
             &pOldPerIoData,
             INFINITE);
         if(!bRet && !pOldPerIoData)//没有从完成队列中取到任何东西。
         {
             continue;
         }
         if(bRet == ERROR_SUCCESS &&
             BytesTransferred == 0 &&
             pOldPerIoData)//断开连接
         {
             closesocket(pOldPerIoData->Socket);             //关闭socket
             g_session.remove(lpSession);                 //从Session链表中删除
             g_per_handle_data.free_particle(lpSession);     //将Session归还到池
             g_per_io_data.free_particle(pOldPerIoData);

         }
         switch (pOldPerIoData->OperationType)
         {
         case OP_ACCEPT://有连接进来
             {
                 if(!bRet)//连接失败
                 {
                     closesocket(pOldPerIoData->Socket);
                     g_per_io_data.free_particle(pOldPerIoData);
                     break;
                 }
                 //用每个连接(客户端)相关的数据(g_per_handle_data)帮定完成键
                 lpSession=g_per_handle_data.alloc_particle();
                 lpSession->Socket = pOldPerIoData->Socket;//就是我们AcceptEx时传入的socket
                 g_session.push_front(lpSession);
                 if(!CreateIoCompletionPort(
                     (HANDLE)pOldPerIoData->Socket,
                     CompletionPort,
                     (ULONG_PTR)lpSession,
                     count))
                 {
                     closesocket(pOldPerIoData->Socket);             //关闭socket
                     g_session.remove(lpSession);                 //从Session链表中删除
                     g_per_handle_data.free_particle(lpSession);     //将Session归还到池
                 }
                 else
                 {
                     //向完成队列投递异步读操作
                     //分配操作缓存,g_per_io_data与每次读写相关联
                     pNewPerIoData = (LPPER_IO_DATA)g_per_io_data.alloc_particle();
                     pNewPerIoData->Socket = pOldPerIoData->Socket;
                     pNewPerIoData->OperationType = OP_READ;
                     ZeroMemory(&(pNewPerIoData->Overlapped), sizeof(OVERLAPPED));
                     Flags = 0;
                     pNewPerIoData->DataBuf.len = 1024;
                     pNewPerIoData->DataBuf.buf = pNewPerIoData->buffer;
                     pNewPerIoData->lpSession = g_per_handle_data.alloc_particle();
                     if(WSARecv(pNewPerIoData->Socket,
                         &(pNewPerIoData->DataBuf),
                         1,
                         &RecvBytes,
                         &Flags,
                         &(pNewPerIoData->Overlapped),
                         NULL)== SOCKET_ERROR)
                     {
                         if(WSAGetLastError() != ERROR_IO_PENDING)//读操作失败
                         {
                             closesocket(pOldPerIoData->Socket);             //关闭socket
                             g_session.remove(pNewPerIoData->lpSession);     //从Session链表中删除
                             g_per_handle_data.free_particle(pNewPerIoData->lpSession);     //将Session归还到池
                             g_per_io_data.free_particle(pNewPerIoData);//归还OV结构体到内存池
                         }
                     }
                 }
                 g_per_io_data.free_particle(pOldPerIoData);

             }

             break;
         case OP_READ:
             pNewPerIoData = (LPPER_IO_DATA)g_per_io_data.alloc_particle();
             if (pNewPerIoData)
             {
                 pNewPerIoData->Socket = pOldPerIoData->Socket;
                 pNewPerIoData->OperationType = OP_READ;
                 ZeroMemory(&(pNewPerIoData->Overlapped), sizeof(OVERLAPPED));
                 Flags = 0;
                 pNewPerIoData->DataBuf.len = 1024;
                 pNewPerIoData->DataBuf.buf = pNewPerIoData->buffer;
                 pNewPerIoData->OperationType = 0; // read
                 pNewPerIoData->lpSession =   pOldPerIoData->lpSession;
                 if(WSARecv(pNewPerIoData->Socket,
                     &(pNewPerIoData->DataBuf),
                     1,
                     &RecvBytes,
                     &Flags,
                     &(pNewPerIoData->Overlapped),
                     NULL)== SOCKET_ERROR)
                 {
                     if(WSAGetLastError() != ERROR_IO_PENDING)
                     {
                         closesocket(pOldPerIoData->Socket);             //关闭socket
                         g_session.remove(pNewPerIoData->lpSession);     //从Session链表中删除
                         g_per_handle_data.free_particle(pNewPerIoData->lpSession);     //将Session归还到池
                         g_per_io_data.free_particle(pNewPerIoData);//归还OV结构体到内存池
                     }
                 }
                 cout << pOldPerIoData->DataBuf.buf << endl;
                 send(pOldPerIoData->Socket,"9876543210",lstrlen("9876543210")+1,0);
                 g_per_io_data.free_particle(pOldPerIoData);
             }
            
             break;
         case OP_WRITE:

             break;
         default:
             break;
         }
        

     }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值