//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* ¤t_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* ¤t_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;
}
}
}