青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 126,  comments - 73,  trackbacks - 0
<<3D游戲程序設(shè)計(jì)大師技巧>>這本書.
網(wǎng)上討論完成端口資料很多,大寶收集的最多,有些是錯(cuò)誤的,有些說的比較模糊.我就進(jìn)可能清晰說明一下完成端口在游戲開發(fā)中一般模型.并解

決幾個(gè)難點(diǎn)問題.
由于完成端口是多線模型(當(dāng)然可以把工作線程設(shè)定為一個(gè))所以設(shè)計(jì)到資源的時(shí)候要線程安全,所以我開始簡(jiǎn)單封裝了一下幾個(gè)stl的容器.
/***************************************以下代碼要與后邊的代碼一起編譯*******************************************/
//memorypool.h
//particle_allocator.h
#pragma once
#include <iostream>
#include <list>
#include <queue>
#include <windows.h>
using namespace std;

//關(guān)鍵區(qū)鎖
class CLock
{
????CRITICAL_SECTION _crisection;
public:
????CLock()????
????{
????InitializeCriticalSection( &_crisection );
????}
????~CLock()??
????{
????DeleteCriticalSection( &_crisection );????
????}
????void Lock()
????{
????EnterCriticalSection( &_crisection );??????
????}
????void Unlock()
????{
????LeaveCriticalSection( &_crisection );??????
????}
};

//鏈表模板
template <class T>
class CList : public list<T>
{

public:
????CList(void){};
????virtual ~CList(void){};
????CLock _guard;

????DWORD Size()
????{
????DWORD dwSize = 0;
????_guard.Lock();
????dwSize = size();
????_guard.Unlock();
????return dwSize;
????};
????void Clear()
????{
????_guard.Lock();
????clear();
????_guard.Unlock();
????}

????//添加數(shù)據(jù)到鏈表末尾,并返回添加后的該數(shù)據(jù)的節(jié)點(diǎn)指針
????iterator Push_Back(T lpData)
????{
????iterator lpNode = NULL;
????_guard.Lock();
????lpNode = insert(end(), lpData);
????_guard.Unlock();
????return lpNode;
????};

????//刪除一個(gè)節(jié)點(diǎn)
????void Erase(iterator lpNode)
????{
????_guard.Lock();
????erase(lpNode);
????_guard.Unlock();
????};

};

template <class T>
class CQueue : private CLock
{
private:
????queue<T> m_Queue;

public:
????CQueue(void) {};
????virtual ~CQueue(void) {};

????DWORD GetSize()
????{
????return (DWORD)m_Queue.size();
????};

????virtual void Push(T lpData)
????{
????Lock();
????m_Queue.push(lpData);
????Unlock();
????};

????virtual T Pop()
????{
????T lpData = NULL;
????Lock();
????if(m_Queue.size())
????{
????????lpData = m_Queue.front();
????????m_Queue.pop();
????}
????Unlock();
????return lpData;
????};
};
template<class ElementClass, int NumElement>
class CMemoryPool
{
????enum
????{
????chunk_size =NumElement
????};
????typedef unsigned char byte;
????list<byte *>??chunks;//內(nèi)存指針
????queue<ElementClass *> free_list;//空閑塊隊(duì)列
????CLock guard;
public:
????CMemoryPool()
????{
????InitMemPool();
????}
????~CMemoryPool()
????{
????DestroyMemPool();
????}
????//初時(shí)化內(nèi)存池
????void InitMemPool()
????{
????guard.Lock();
????used = 0;
????free = 0;
????//byte *memory;
????//memory = new byte[chunk_size*sizeof(ElementClass)];
????//if (!memory)
????//{
????//????cout << "內(nèi)存分配出錯(cuò)....."<< endl;
????//????return ;
????//}
????//chunks.push_front(memory);
????//for(int i =0;i< chunk_size;i++,free++)
????//{
????//????ElementClass *newnode = (ElementClass *)(memory + i*sizeof(ElementClass));
????//????free_list.push(newnode);

????//}

????guard.Unlock();
????}
????//銷毀內(nèi)存池
????void DestroyMemPool()
????{
????while(!free_list.empty())
????{
????????free_list.pop();
????}
????for(std::list<byte *>::iterator all_iter = chunks.begin();all_iter!=chunks.end();++all_iter)
????{
????????delete [](*all_iter);
????}
????chunks.clear();
????used = 0;
????free= 0;
????}
????//從內(nèi)存池里分配一塊內(nèi)存
????ElementClass* MemPoolAlloc()
????{
????guard.Lock();
????byte *memory;
????if(free_list.empty())
????{
????????memory = new byte[chunk_size*sizeof(ElementClass)];
????????if (!memory)
????????{
????????cout << "內(nèi)存分配出錯(cuò)....."<< endl;
????????return NULL;
????????}
????????chunks.push_front(memory);
????????for(int i =0;i< chunk_size;i++,free++)
????????{
????????ElementClass *newnode = (ElementClass *)(memory + i*sizeof(ElementClass));
????????free_list.push(newnode);

????????}
????}
????ElementClass??*redata = NULL;
????redata = free_list.front();
????ZeroMemory(redata,sizeof(ElementClass));
????free_list.pop();
????++used;
????--free;
????guard.Unlock();
????return redata;
????}
????//還原內(nèi)存池
????void MemPoolFree(ElementClass??*p)
????{
????guard.Lock();
????ZeroMemory(p,sizeof(ElementClass));
????free_list.push(p);
????-- used;
????++ free;
????guard.Unlock();
????}
????//內(nèi)存池信息統(tǒng)計(jì).
????void GetMemPoolInfo()
????{
????cout << "used is "
????????<< used
????????<< " ;??free is "
????????<< free << endl;
????}
private:
????UINT used;
????UINT free;
};
/********************************************************************************************************************/
代碼都非常簡(jiǎn)單.相信有點(diǎn)c++基礎(chǔ)的朋友都能看懂.我只是簡(jiǎn)單說一下內(nèi)存池代碼.內(nèi)存池里ElementClass類型一定要結(jié)構(gòu)形的.也就是那種POD

類型還是什么類型我剛剛聽說這個(gè)名詞,所以記不住.內(nèi)存池第一次分派的時(shí)候,分派NumElement>個(gè)ElementClass類型節(jié)點(diǎn),這些節(jié)點(diǎn)構(gòu)成一個(gè)隊(duì)

列free_list,這些都是通過模版參數(shù)傳入的.每次用的時(shí)候就就從頭找出一個(gè)節(jié)點(diǎn),釋放的時(shí)候又在尾部加入一個(gè)節(jié)點(diǎn),很好理解.用完的時(shí)候再

分配NumElement>個(gè)ElementClass類型節(jié)點(diǎn),節(jié)點(diǎn)到最后統(tǒng)一釋放.

接下來我們看看程序用到的頭文件
//iocpsever.cpp
#include <winsock2.h>
#include "memorypool.h"
#include <Mswsock.h>
#include <process.h>

using namespace std;

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

using namespace std;
1.頭文件有一個(gè)郁悶的問題就是winsock2.h與windows互相包含問題.不知道有什么完美解決辦法,我再項(xiàng)目中用afxsock.h替代winsock2.h.
需要用到庫(kù)兩個(gè)庫(kù)文件我們都通過編譯器指令連接進(jìn)來.
2.完成端口模型用一句話來講就是,把所有的socket都綁定到完成端口上,通過完成端口統(tǒng)一來處理.綁定以后這些socket上有任何的情況,都通

過完成端口反映出來,達(dá)到簡(jiǎn)化處理邏輯的作用.完成端口確實(shí)不會(huì)讓你失望,邏輯結(jié)構(gòu)非常簡(jiǎn)單.
3.我們首先為socket進(jìn)行分類.服務(wù)器的socket可以分為三類,一類就是用于監(jiān)聽的socket,監(jiān)聽socket綁定時(shí)機(jī)是它成功綁定到某一個(gè)端口上的

時(shí)候我們就可以把它綁定到完成端口上.所以我把它放進(jìn)初始化間斷.第二類客戶連接進(jìn)入的socket,這個(gè)只能放進(jìn)當(dāng)連接進(jìn)入完成端口,accept

事件觸發(fā)的時(shí)候我們把它綁定到完成端口上.第三類就是服務(wù)器連接出去的socket,我把它放進(jìn)線程里,在線程內(nèi)部進(jìn)行綁定,因?yàn)檫@類socket相

對(duì)比較少.
4.接下來我們考慮連接問題.由于完成端口是一種異步模型,它工作機(jī)制和我們傳統(tǒng)的同步socket連接模型不一樣,我們先投遞許多連接消息,當(dāng)

連接成功的時(shí)候,對(duì)應(yīng)的完成端口上發(fā)生accept事件.這里的accept是我們自己定義的,并且在AcceptEx的時(shí)候通過重疊結(jié)構(gòu)傳入的,你也可以任

意定義,但是我想你不會(huì)把a(bǔ)ccept定義成send,而send定義成 receive吧.由于我們?cè)贏cceptEx的時(shí)候傳入單io數(shù)據(jù)結(jié)構(gòu),所以一定要關(guān)閉完成端

口的時(shí)候把它釋放掉.關(guān)于資源的釋放我們放在下邊來考慮.話題不要扯遠(yuǎn),回來連接上來,我們到底該怎么樣投遞呢.如果我們一下投遞的過多,

就會(huì)造成資源浪費(fèi),過少如果用完該怎么辦.這樣客戶就不能那個(gè)連接了.通用的做法就是每次投遞10個(gè),如果用完繼續(xù)投機(jī)10個(gè).怎么樣才能知道

用完呢?需要在監(jiān)聽socket上注冊(cè)一個(gè)FD_ACCEPT事件,當(dāng)投遞用完的時(shí)候事件觸發(fā).這就就可以繼續(xù)投機(jī),我也把它放進(jìn)線程里邊去做這件事.
????g_hAcceptExOverEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
????if(!g_hAcceptExOverEvent)
????{
????return false;
????}
????//幫定事件,由于我們用AcceptEx是一個(gè)異步的投遞,這樣幫定之后,如果投遞的AcceptEx事件全部完成
????//則g_hAcceptExOverEvent事件得到通知,進(jìn)而同步AcceptEx調(diào)用
????if(WSAEventSelect(Listen, g_hAcceptExOverEvent, FD_ACCEPT) == SOCKET_ERROR)
????{
????return false;
????}
????//由于開始是復(fù)位,變成置位
????SetEvent(g_hAcceptExOverEvent);
5.最后討論資源釋放問題,分析如下代碼
????closesocket(Listen);
????g_session._guard.Lock();
????for(CList<LPPER_HANDLE_DATA>::iterator lpNode = g_session.begin(); lpNode != g_session.end();lpNode++)
????{
????closesocket((*lpNode)->Socket);
????printf(" close socket??= %d\n",(*lpNode)->Socket);
????}
????g_session._guard.Unlock();
????Sleep(1000);
????//向IOCP發(fā)送結(jié)束線程信號(hào)

????for(DWORD i = 0; i < threadcnt; i++)
????PostQueuedCompletionStatus(CompletionPort, 0, NULL, NULL);

????//等待工作線程結(jié)束,等待時(shí)間10秒

????if(WaitForMultipleObjects(threadcnt, m_arrayIOCPThreadHandle, TRUE, 10000) != WAIT_OBJECT_0)
????{
????//如果10秒內(nèi)沒有結(jié)束所有線程,就強(qiáng)制結(jié)束
????for(DWORD i = 0; i < threadcnt; i++)
????????TerminateThread(m_arrayIOCPThreadHandle[i], 0);
????}
????//關(guān)閉所有工作線程句柄
????for(DWORD i = 0; i < threadcnt; i++)
????CloseHandle(m_arrayIOCPThreadHandle[i]);
????//釋放線程句柄數(shù)組
????delete [] m_arrayIOCPThreadHandle;
????m_arrayIOCPThreadHandle = NULL;
????CloseHandle(CompletionPort);
????g_per_io_data.DestroyMemPool();
????g_per_handle_data.DestroyMemPool();
????g_session.Clear();
我們也分三類來考慮
首先第一類監(jiān)聽socket,監(jiān)聽socket關(guān)閉之后,由于連接事件與監(jiān)聽socket有關(guān)系.所以所有未完成的連接都會(huì)從完成端口返回來.這些連接里我

們分配了單io數(shù)據(jù)結(jié)構(gòu)一定要釋放.
釋放邏輯如下

bRet = GetQueuedCompletionStatus(CompletionPort,
????????&BytesTransferred,
????????(PULONG_PTR)
????????&lpCompletionKey,
????????(LPOVERLAPPED*)
????????&lpPerIoData,
????????INFINITE);
????if(!bRet)//沒有取到任何東西
????{
????????if(!lpPerIoData)
????????continue;
????}
????//收到線程結(jié)束信號(hào)
????if(!lpCompletionKey)
????????return 0;

????LPPER_HANDLE_DATA lpSession = (LPPER_HANDLE_DATA)lpCompletionKey;

????if(!bRet)
????{
????????if(BytesTransferred == 0 )//斷開連接
????????{
????????if (lpPerIoData->OperationType == OP_READ)
????????{
????????????printf("client socket %d disconnect\n",lpPerIoData->Socket);
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????continue;
????????}

????????}
????}
????switch (lpPerIoData->OperationType)
????{
????case OP_ACCEPT://有連接進(jìn)來
????????{
????????if (!bRet)
????????{
????????????closesocket(lpPerIoData->Socket);?? //關(guān)閉socket
????????????g_per_io_data.MemPoolFree(lpPerIoData);????//歸還結(jié)構(gòu)體到內(nèi)存池
????????????printf("關(guān)閉已經(jīng)投遞空閑連接\n");
????????????break;
????????}
.........................后邊代碼省略掉當(dāng)關(guān)閉監(jiān)聽socket的時(shí)候GetQueuedCompletionStatus會(huì)返回false并且BytesTransferred等于零,

并且lpPerIoData,不為空lpPerIoData->OperationType操作符號(hào)就是我們AcceptEx傳入的自定義操作類型accept,這樣就釋放掉了與連接相關(guān)的

資源.所以我們?cè)陉P(guān)掉監(jiān)聽socket之后要sleep一下讓資源能充分釋放.
第二類就是客戶socket.
分兩種情況考慮.(1)服務(wù)器主動(dòng)關(guān)閉,這又可以分為兩種情況,第一,當(dāng)你在工作線程里關(guān)掉socket的時(shí)候GetQueuedCompletionStatus是收不到

任何消息的,所以直接釋放資源就是了.這樣產(chǎn)生的一個(gè)問題就是如果你把拆包邏輯放進(jìn)完成端口工作線程的時(shí)候,當(dāng)數(shù)據(jù)包發(fā)生錯(cuò)誤的時(shí)候,你

想關(guān)閉socket,一定要記住連釋放掉與這個(gè)socket相關(guān)的資源.
if(WSARecv(lpPerIoData->Socket,
????????????&(lpPerIoData->DataBuf),
????????????1,
????????????&lpPerIoData->RecvBytes,
????????????&lpPerIoData->Flags,
????????????&(lpPerIoData->Overlapped),
????????????NULL)== SOCKET_ERROR)
????????????{
????????????if(WSAGetLastError() != ERROR_IO_PENDING)//讀操作失敗
????????????{
????????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????????
????????????}
????????????}
第二,就是當(dāng)在其他線程里關(guān)閉的時(shí)候,GetQueuedCompletionStatus就會(huì)收到消息.由于我們每次收到自定義recv事件的時(shí)候,接著就投遞了一個(gè)
recv事件.所以這個(gè)事件失敗消息肯定會(huì)從完成端口返回.處理如下
bRet = GetQueuedCompletionStatus(CompletionPort,
????????&BytesTransferred,
????????(PULONG_PTR)
????????&lpCompletionKey,
????????(LPOVERLAPPED*)
????????&lpPerIoData,
????????INFINITE);
????if(!bRet)//沒有取到任何東西
????{
????????if(!lpPerIoData)
????????continue;
????}
????//收到線程結(jié)束信號(hào)
????if(!lpCompletionKey)
????????return 0;

????LPPER_HANDLE_DATA lpSession = (LPPER_HANDLE_DATA)lpCompletionKey;

????if(!bRet)
????{
????????if(BytesTransferred == 0 )//斷開連接
????????{
????????if (lpPerIoData->OperationType == OP_READ)
????????{
????????????printf("client socket %d disconnect\n",lpPerIoData->Socket);
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????continue;
????????}

????????}
????}
返回代碼為false并且BytesTransferred為零且lpPerIoData->OperationType == OP_READ.
(2)用戶斷開的處理邏輯與服務(wù)器斷開第二種情況是一樣的.有些資料講服務(wù)器主動(dòng)斷開的時(shí)候GetQueuedCompletionStatus會(huì)返回true是錯(cuò)誤的

.
6.ERROR_IO_PENDING消息處理這個(gè)消息說明現(xiàn)在這個(gè)重疊操作還沒有完成,我們必須進(jìn)行等待,所以必須在錯(cuò)誤判斷的時(shí)候忽略掉這個(gè)消息.
????????????if(WSAGetLastError() != ERROR_IO_PENDING)//讀操作失敗
????????????{
????????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????????
????????????}
7.GetQueuedCompletionStatus返回之后的邏輯判斷按下邊的代碼進(jìn)行,我們必須知道為什么這樣做,理由就是上邊六條再加上msdn里

GetQueuedCompletionStatus這個(gè)函數(shù)的說明.
DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{
????DWORD BytesTransferred;
????LPPER_IO_DATA lpPerIoData = NULL;
????LPVOID lpCompletionKey = NULL;
????BOOL bRet = FALSE;

????while (1)
????{
????bRet = GetQueuedCompletionStatus(CompletionPort,
????????&BytesTransferred,
????????(PULONG_PTR)
????????&lpCompletionKey,
????????(LPOVERLAPPED*)
????????&lpPerIoData,
????????INFINITE);
????if(!bRet)//沒有取到任何東西
????{
????????if(!lpPerIoData)
????????continue;
????}
????//收到線程結(jié)束信號(hào)
????if(!lpCompletionKey)
????????return 0;

????LPPER_HANDLE_DATA lpSession = (LPPER_HANDLE_DATA)lpCompletionKey;

????if(!bRet)
????{
????????if(BytesTransferred == 0 )//斷開連接
????????{
????????if (lpPerIoData->OperationType == OP_READ)
????????{
????????????printf("client socket %d disconnect\n",lpPerIoData->Socket);
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????continue;
????????}

????????}
????}
????switch (lpPerIoData->OperationType)
????{
????case OP_ACCEPT://有連接進(jìn)來
????????{
????????if (!bRet)
????????{
????????????closesocket(lpPerIoData->Socket);?? //關(guān)閉socket
????????????g_per_io_data.MemPoolFree(lpPerIoData);????//歸還結(jié)構(gòu)體到內(nèi)存池
????????????printf("關(guān)閉已經(jīng)投遞空閑連接\n");
????????????break;
????????}
????????LPPER_HANDLE_DATA lpCurSession;
????????lpCurSession=g_per_handle_data.MemPoolAlloc();
????????printf(" connect socket??= %d\n",lpPerIoData->Socket);
????????lpCurSession->Socket = lpPerIoData->Socket;//就是我們AcceptEx時(shí)傳入的socket
????????lpCurSession->node = g_session.Push_Back(lpCurSession);
????????lpPerIoData->lpSession = lpCurSession;
????????if(!CreateIoCompletionPort(
????????????(HANDLE)lpPerIoData->Socket,
????????????CompletionPort,
????????????(ULONG_PTR)lpCurSession,
????????????0))
????????{
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_per_handle_data.MemPoolFree(lpSession);
????????}
????????else
????????{
????????????//g_per_io_data與每次讀寫相關(guān)聯(lián)
????????????lpPerIoData->OperationType = OP_READ;
????????????ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
????????????lpPerIoData->Flags = 0;
????????????lpPerIoData->DataBuf.len = 1024;
????????????lpPerIoData->DataBuf.buf = lpPerIoData->buffer;
????????????lpPerIoData->RecvBytes =0;
????????????lpPerIoData->lpSession = lpSession;
????????????ZeroMemory(lpPerIoData->buffer,1024);

????????????if(WSARecv(lpPerIoData->Socket,
????????????&(lpPerIoData->DataBuf),
????????????1,
????????????&lpPerIoData->RecvBytes,
????????????&lpPerIoData->Flags,
????????????&(lpPerIoData->Overlapped),
????????????NULL)== SOCKET_ERROR)
????????????{
????????????if(WSAGetLastError() != ERROR_IO_PENDING)//讀操作失敗
????????????{
????????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????????
????????????}
????????????}

????????}

????????}
????????break;
????case OP_READ:
????????{
????????//cout << lpPerIoData->DataBuf.buf << endl;
????????send(lpPerIoData->Socket,"9876543210",lstrlen("9876543210")+1,0);
????????lpPerIoData->OperationType = OP_READ;
????????ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
????????lpPerIoData->Flags = 0;
????????lpPerIoData->DataBuf.len = 1024;
????????lpPerIoData->DataBuf.buf = lpPerIoData->buffer;
????????lpPerIoData->RecvBytes =0;
????????lpPerIoData->lpSession = lpSession;
????????ZeroMemory(lpPerIoData->buffer,1024);

????????if(WSARecv(lpPerIoData->Socket,
????????????&(lpPerIoData->DataBuf),
????????????1,
????????????&lpPerIoData->RecvBytes,
????????????&lpPerIoData->Flags,
????????????&(lpPerIoData->Overlapped),
????????????NULL)== SOCKET_ERROR)
????????{
????????????if(WSAGetLastError() != ERROR_IO_PENDING)
????????????{
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????}
????????}
????????}
????????break;
????case OP_WRITE:
????????break;
????default:
????????break;
????}
????}
}
下邊就是整個(gè)控制臺(tái)代碼.
/********************************以下代碼要與前邊的容器頭文件一起進(jìn)行編譯***********************************************/
//iocpsever.cpp
#include <winsock2.h>
#include "memorypool.h"
#include <Mswsock.h>
#include <process.h>

using namespace std;

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

using namespace std;


// 單句柄數(shù)據(jù),每個(gè)連接(客戶端)對(duì)應(yīng)一個(gè)這樣的結(jié)構(gòu)。
//只有連接斷開,或者服務(wù)器關(guān)閉的時(shí)候才釋放
struct tagPER_HANDLE_DATA;

typedef unsigned (WINAPI *PBEGINTHREADX_THREADFUN)(LPVOID lpThreadParameter); //線程函數(shù)原型

typedef struct tagPER_HANDLE_DATA *LPPER_HANDLE_DATA;
typedef CList<LPPER_HANDLE_DATA>::iterator LISTSESSIONNODE;

typedef struct tagPER_HANDLE_DATA
{
????SOCKET Socket;
????LISTSESSIONNODE node;
????// 將和這個(gè)句柄關(guān)聯(lián)的其他有用信息,盡管放在這里面吧
}PER_HANDLE_DATA;

// 單I/O操作數(shù)據(jù)。每次收發(fā)數(shù)據(jù)的時(shí)候
//收發(fā)數(shù)據(jù)操作完成數(shù)之后釋放。
//需要注意的是OVERLAPPED Overlapped一定要放在最前

typedef struct tagPER_IO_DATA
{
????OVERLAPPED Overlapped;
????WSABUF DataBuf;
????char buffer[1024];
????DWORD RecvBytes;
????DWORD Flags;
????int OperationType;
????SOCKET Socket;
????LPPER_HANDLE_DATA lpSession;
}PER_IO_DATA,*LPPER_IO_DATA;
//操作標(biāo)志
#define OP_READ 0
#define OP_WRITE 1
#define OP_ACCEPT 2

//連接數(shù)據(jù)內(nèi)存池
CMemoryPool<PER_IO_DATA,1024> g_per_io_data;
//數(shù)據(jù)收發(fā)內(nèi)存池
CMemoryPool<PER_HANDLE_DATA,1024> g_per_handle_data;
//完成線程

//保存當(dāng)前連接數(shù)據(jù)指針
CList<LPPER_HANDLE_DATA> g_session;


SOCKET Listen;
HANDLE CompletionPort;
HANDLE *m_arrayIOCPThreadHandle;
DWORD threadcnt;
DWORD WINAPI ServerWorkerThread(LPVOID lpParam);


BOOL b;
HANDLE g_hAcceptExOverEvent;
HANDLE g_hAcceptExThread;
DWORD WINAPI WinSockAcceptEXThread(LPVOID lpParam);
bool StartUpWinSockAcceptEXThread();
void CloseWinSockAcceptEXThread();
void InitCompletionPort();
void CloseCompletionPort();
int main(void)
{
????InitCompletionPort();
????StartUpWinSockAcceptEXThread();

????{
????HANDLE hWaitEvent;
????hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
????WaitForSingleObject(hWaitEvent, 30000/*INFINITE*/);
????CloseHandle(hWaitEvent);

????}
????CloseCompletionPort();
????return 0;
}
DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{
????DWORD BytesTransferred;
????LPPER_IO_DATA lpPerIoData = NULL;
????LPVOID lpCompletionKey = NULL;
????BOOL bRet = FALSE;

????while (1)
????{
????bRet = GetQueuedCompletionStatus(CompletionPort,
????????&BytesTransferred,
????????(PULONG_PTR)
????????&lpCompletionKey,
????????(LPOVERLAPPED*)
????????&lpPerIoData,
????????INFINITE);
????if(!bRet)//沒有取到任何東西
????{
????????if(!lpPerIoData)
????????continue;
????}
????//收到線程結(jié)束信號(hào)
????if(!lpCompletionKey)
????????return 0;

????LPPER_HANDLE_DATA lpSession = (LPPER_HANDLE_DATA)lpCompletionKey;

????if(!bRet)
????{
????????if(BytesTransferred == 0 )//斷開連接
????????{
????????if (lpPerIoData->OperationType == OP_READ)
????????{
????????????printf("client socket %d disconnect\n",lpPerIoData->Socket);
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????continue;
????????}

????????}
????}
????switch (lpPerIoData->OperationType)
????{
????case OP_ACCEPT://有連接進(jìn)來
????????{
????????if (!bRet)
????????{
????????????closesocket(lpPerIoData->Socket);?? //關(guān)閉socket
????????????g_per_io_data.MemPoolFree(lpPerIoData);????//歸還結(jié)構(gòu)體到內(nèi)存池
????????????printf("關(guān)閉已經(jīng)投遞空閑連接\n");
????????????break;
????????}
????????LPPER_HANDLE_DATA lpCurSession;
????????lpCurSession=g_per_handle_data.MemPoolAlloc();
????????printf(" connect socket??= %d\n",lpPerIoData->Socket);
????????lpCurSession->Socket = lpPerIoData->Socket;//就是我們AcceptEx時(shí)傳入的socket
????????lpCurSession->node = g_session.Push_Back(lpCurSession);
????????lpPerIoData->lpSession = lpCurSession;
????????if(!CreateIoCompletionPort(
????????????(HANDLE)lpPerIoData->Socket,
????????????CompletionPort,
????????????(ULONG_PTR)lpCurSession,
????????????0))
????????{
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_per_handle_data.MemPoolFree(lpSession);
????????}
????????else
????????{
????????????//g_per_io_data與每次讀寫相關(guān)聯(lián)
????????????lpPerIoData->OperationType = OP_READ;
????????????ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
????????????lpPerIoData->Flags = 0;
????????????lpPerIoData->DataBuf.len = 1024;
????????????lpPerIoData->DataBuf.buf = lpPerIoData->buffer;
????????????lpPerIoData->RecvBytes =0;
????????????lpPerIoData->lpSession = lpSession;
????????????ZeroMemory(lpPerIoData->buffer,1024);

????????????if(WSARecv(lpPerIoData->Socket,
????????????&(lpPerIoData->DataBuf),
????????????1,
????????????&lpPerIoData->RecvBytes,
????????????&lpPerIoData->Flags,
????????????&(lpPerIoData->Overlapped),
????????????NULL)== SOCKET_ERROR)
????????????{
????????????if(WSAGetLastError() != ERROR_IO_PENDING)//讀操作失敗
????????????{
????????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????????
????????????}
????????????}

????????}

????????}
????????break;
????case OP_READ:
????????{
????????//cout << lpPerIoData->DataBuf.buf << endl;
????????send(lpPerIoData->Socket,"9876543210",lstrlen("9876543210")+1,0);
????????lpPerIoData->OperationType = OP_READ;
????????ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
????????lpPerIoData->Flags = 0;
????????lpPerIoData->DataBuf.len = 1024;
????????lpPerIoData->DataBuf.buf = lpPerIoData->buffer;
????????lpPerIoData->RecvBytes =0;
????????lpPerIoData->lpSession = lpSession;
????????ZeroMemory(lpPerIoData->buffer,1024);

????????if(WSARecv(lpPerIoData->Socket,
????????????&(lpPerIoData->DataBuf),
????????????1,
????????????&lpPerIoData->RecvBytes,
????????????&lpPerIoData->Flags,
????????????&(lpPerIoData->Overlapped),
????????????NULL)== SOCKET_ERROR)
????????{
????????????if(WSAGetLastError() != ERROR_IO_PENDING)
????????????{
????????????closesocket(lpPerIoData->Socket); //關(guān)閉socket
????????????g_session.Erase(lpSession->node); //從Session鏈表中刪除
????????????g_per_handle_data.MemPoolFree(lpSession); //將Session歸還到池
????????????g_per_io_data.MemPoolFree(lpPerIoData);
????????????}
????????}
????????}
????????break;
????case OP_WRITE:
????????break;
????default:
????????break;
????}
????}
}
DWORD WINAPI WinSockAcceptEXThread(LPVOID lpParam)
{
????LINGER lingerStruct = { 0x01, 0x00 };
????BOOL bNodelay = TRUE;
????//創(chuàng)建事件
????while (b)
????{
????//每次投遞10次,進(jìn)入等待狀態(tài),當(dāng)AcceptEx全部完成之后,繼續(xù)投遞
????if(WaitForSingleObject(g_hAcceptExOverEvent, INFINITE) == WAIT_FAILED)
????????continue;
????for(int i =0;i<10 && b;i++)
????{
????????int zero =0;
????????PER_IO_DATA * pper_io_data = NULL;
????????DWORD dwAddrLen = sizeof(sockaddr_in)+16;
????????pper_io_data = g_per_io_data.MemPoolAlloc();
????????pper_io_data ->Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
????????if(pper_io_data->Socket == INVALID_SOCKET)
????????{
????????g_per_io_data.MemPoolFree(pper_io_data);
????????continue;
????????}
????????pper_io_data->OperationType = OP_ACCEPT;
????????pper_io_data->lpSession=NULL;
????????ZeroMemory(&pper_io_data->Overlapped,sizeof(OVERLAPPED));//一定要注意清零
????????pper_io_data->RecvBytes =0;
????????pper_io_data->Flags =0;
????????ZeroMemory(pper_io_data->buffer,1024);
????????setsockopt(pper_io_data->Socket, IPPROTO_TCP, TCP_NODELAY, (char*)&bNodelay, sizeof(BOOL));
????????setsockopt(pper_io_data->Socket, SOL_SOCKET, SO_LINGER, (char*)&lingerStruct, sizeof(LINGER));

????????if(!AcceptEx(Listen,pper_io_data ->Socket,pper_io_data ->buffer,0,dwAddrLen,dwAddrLen,&pper_io_data -

>RecvBytes,&pper_io_data->Overlapped))
????????{
????????if(WSAGetLastError() != ERROR_IO_PENDING)//對(duì)于AcceptEx,WSARecv,WSASend一定要有這樣的判斷,因?yàn)槭钱惒降乃?br />
不會(huì)立即完成
????????{
????????????closesocket(pper_io_data->Socket);
????????????g_per_io_data.MemPoolFree(pper_io_data); //歸還結(jié)構(gòu)體到內(nèi)存池
????????????continue;
????????}

????????}
????}
????}
????return 0;
}
bool StartUpWinSockAcceptEXThread()
{
????g_hAcceptExOverEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
????if(!g_hAcceptExOverEvent)
????{
????return false;
????}
????//幫定事件,由于我們用AcceptEx是一個(gè)異步的投遞,這樣幫定之后,如果投遞的AcceptEx事件全部完成
????//則g_hAcceptExOverEvent事件得到通知,進(jìn)而同步AcceptEx調(diào)用
????if(WSAEventSelect(Listen, g_hAcceptExOverEvent, FD_ACCEPT) == SOCKET_ERROR)
????{
????return false;
????}
????//由于開始是復(fù)位,變成置位
????SetEvent(g_hAcceptExOverEvent);
????b = TRUE;
????g_hAcceptExThread = (HANDLE)_beginthreadex(NULL, 0, (PBEGINTHREADX_THREADFUN)WinSockAcceptEXThread, NULL, 0, NULL);
????if(!g_hAcceptExThread)
????return false;
????return true;

}
void CloseWinSockAcceptEXThread()
{
????b=false;
????SetEvent(g_hAcceptExOverEvent);
????if(WaitForSingleObject(g_hAcceptExThread,10000)!= WAIT_OBJECT_0)
????TerminateThread(g_hAcceptExThread, 0);
????CloseHandle(g_hAcceptExThread);
????CloseHandle(g_hAcceptExOverEvent);
};
void CloseCompletionPort()
{
????
????closesocket(Listen);
????g_session._guard.Lock();
????for(CList<LPPER_HANDLE_DATA>::iterator lpNode = g_session.begin(); lpNode != g_session.end();lpNode++)
????{
????closesocket((*lpNode)->Socket);
????printf(" close socket??= %d\n",(*lpNode)->Socket);
????}
????g_session._guard.Unlock();
????Sleep(1000);
????//向IOCP發(fā)送結(jié)束線程信號(hào)

????for(DWORD i = 0; i < threadcnt; i++)
????PostQueuedCompletionStatus(CompletionPort, 0, NULL, NULL);

????//等待工作線程結(jié)束,等待時(shí)間10秒

????if(WaitForMultipleObjects(threadcnt, m_arrayIOCPThreadHandle, TRUE, 10000) != WAIT_OBJECT_0)
????{
????//如果10秒內(nèi)沒有結(jié)束所有線程,就強(qiáng)制結(jié)束
????for(DWORD i = 0; i < threadcnt; i++)
????????TerminateThread(m_arrayIOCPThreadHandle[i], 0);
????}
????//關(guān)閉所有工作線程句柄
????for(DWORD i = 0; i < threadcnt; i++)
????CloseHandle(m_arrayIOCPThreadHandle[i]);
????//釋放線程句柄數(shù)組
????delete [] m_arrayIOCPThreadHandle;
????m_arrayIOCPThreadHandle = NULL;
????CloseHandle(CompletionPort);
????g_per_io_data.DestroyMemPool();
????g_per_handle_data.DestroyMemPool();
????g_session.Clear();
}
void InitCompletionPort()
{
????WSADATA wsd;
????SYSTEM_INFO SystemInfo;
????SOCKADDR_IN InternetAddr;
????WSAStartup(MAKEWORD(2, 2), &wsd);

????//創(chuàng)建完成端口
????CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
????NULL,
????0,
????threadcnt);

????//得到處理器數(shù)量
????GetSystemInfo(&SystemInfo);
????//經(jīng)驗(yàn)公式:一般按公式創(chuàng)建工作線程

????threadcnt = 2*SystemInfo.dwNumberOfProcessors+2;
????m_arrayIOCPThreadHandle = new HANDLE[threadcnt];
????for(DWORD i = 0; i < threadcnt; i++)
????{
????m_arrayIOCPThreadHandle[i] = (HANDLE)_beginthreadex(NULL, 0, (PBEGINTHREADX_THREADFUN)ServerWorkerThread, NULL, 0,

NULL);
????if(!m_arrayIOCPThreadHandle[i])
????????return ;
????}
????//創(chuàng)建監(jiān)聽socket

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

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

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

????//開始監(jiān)聽
????listen(Listen, SOMAXCONN);
????//完成端口幫定到監(jiān)聽socket
????printf(" listen socket??= %d\n",Listen);
????if (CreateIoCompletionPort((HANDLE) Listen, CompletionPort, (ULONG_PTR)&Listen, threadcnt) == NULL)
????{
????printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
????return ;
????}
}




FROM:http://www.vchelp.net/cndevforum/subject_view.asp?subject_id=176818&forum_id=55
作者:lustskyboy溝通無限
posted on 2007-01-31 14:10 我風(fēng) 閱讀(1145) 評(píng)論(2)  編輯 收藏 引用

FeedBack:
# re: (轉(zhuǎn))完成端口模型探討.
2008-12-15 13:22 | yuan_lt2001@sina.com
setsockopt(pper_io_data->Socket, IPPROTO_TCP, TCP_NODELAY, (char*)&bNodelay, sizeof(BOOL));
setsockopt(pper_io_data->Socket, SOL_SOCKET, SO_LINGER, (char*)&lingerStruct, sizeof(LINGER));


我反復(fù)測(cè)試setsockopt:LINGER在WSA_FLAG_OVERLAPPED模式下不起作用, 在高并發(fā)的情況下服務(wù)器會(huì)因過多的TIME_WAIT而造成無SOCKET的可用; 不知道你怎么解決這個(gè)問題的,請(qǐng)發(fā)email給我yuan_lt2001@sina.com,我被TIME_WAIT困擾了好多天了  回復(fù)  更多評(píng)論
  
# re: (轉(zhuǎn))完成端口模型探討.
2009-04-28 17:24 | 過客
好文!頂!  回復(fù)  更多評(píng)論
  

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(12)

隨筆分類

隨筆檔案

文章檔案

相冊(cè)

收藏夾

C++

MyFavorite

搜索

  •  

積分與排名

  • 積分 - 329009
  • 排名 - 75

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美国产精品专区| 麻豆国产精品777777在线| 裸体女人亚洲精品一区| 欧美ed2k| 国产精品久久久久久户外露出 | 亚洲电影第1页| 亚洲经典自拍| 国产精品v日韩精品| 欧美中文字幕在线观看| 亚洲国产成人精品久久久国产成人一区| 日韩视频欧美视频| 久久精品99国产精品| 欧美激情精品久久久久久免费印度| 一本久道综合久久精品| 国产亚洲欧美中文| 欧美紧缚bdsm在线视频| 午夜精品一区二区三区在线| 免费日韩成人| 亚洲综合色自拍一区| 精品不卡在线| 国产精品久久久久久久久免费樱桃| 久久久久在线| 亚洲一区区二区| 亚洲国产毛片完整版| 久久国产一区二区| 一本久久综合| 亚洲第一精品夜夜躁人人躁| 国产精品视频免费观看| 欧美成人亚洲成人| 欧美在线免费看| 一区二区三区日韩精品| 国产精品福利网| 亚洲激情在线激情| 美日韩精品视频| 国产精品一区久久久| 午夜精品亚洲一区二区三区嫩草| 亚洲一区二区高清| 你懂的视频一区二区| 国产美女在线精品免费观看| 欧美一级播放| 99视频一区二区| 女主播福利一区| 一区二区三区在线观看国产| 中文日韩电影网站| 欧美资源在线| 亚洲乱码国产乱码精品精可以看| 亚洲无人区一区| 久久免费国产精品1| 国产精品videosex极品| 亚洲激情黄色| 亚洲国产精品日韩| 美女视频黄免费的久久| 在线播放亚洲一区| 久久久久久91香蕉国产| 在线综合+亚洲+欧美中文字幕| 欧美视频日韩| 蜜桃av综合| 久久综合狠狠| 亚洲一区二区免费在线| 久久午夜视频| 亚洲午夜激情网页| 亚洲欧美国产毛片在线| 国产九色精品成人porny| 99精品国产在热久久婷婷| 欧美激情麻豆| 亚洲一区二区免费看| 欧美小视频在线| 亚洲午夜精品网| 亚洲最快最全在线视频| 欧美色大人视频| 亚洲一区3d动漫同人无遮挡| 午夜精品久久久久99热蜜桃导演| 国产毛片精品视频| 嫩模写真一区二区三区三州| 欧美激情久久久久久| 亚洲已满18点击进入久久| 狠狠色丁香婷婷综合影院| 久久久久久亚洲精品杨幂换脸| 国产精品久久久久久影视| 亚洲精品少妇30p| 国产视频精品网| 久久综合五月| 国产精品一区久久| 亚洲三级影院| 一本色道久久综合狠狠躁篇的优点 | 国产日韩欧美中文| 国产欧美一区二区三区国产幕精品| 国产精品网站一区| 国内精品久久久久久久影视蜜臀| 伊伊综合在线| 亚洲精品久久久久久久久久久久久 | 欧美日韩国产麻豆| 国产精品久久999| 国产日韩欧美在线播放| 影音先锋在线一区| 亚洲精选一区| 亚洲欧美日韩一区二区三区在线观看 | 性色av一区二区三区红粉影视| 久久精品成人| 亚洲国产第一页| 99av国产精品欲麻豆| 小嫩嫩精品导航| 欧美大尺度在线| 欧美视频一区在线观看| 国产欧美日韩精品丝袜高跟鞋| 在线免费观看日本欧美| 99国产精品99久久久久久| 欧美一区二区播放| 欧美国产一区二区| 在线一区二区三区四区| 久久精品一区二区三区中文字幕| 欧美激情精品久久久久久蜜臀| 国产精品乱人伦中文| 在线精品一区二区| 亚洲在线国产日韩欧美| 欧美成人一区二区三区| 亚洲视频香蕉人妖| 蜜桃av久久久亚洲精品| 国产精品美女主播在线观看纯欲| 在线观看福利一区| 亚洲欧美视频在线观看视频| 欧美成人精品福利| 亚洲男人第一av网站| 欧美成人精品一区二区三区| 国产欧美精品xxxx另类| 99re热这里只有精品免费视频| 久久久www成人免费无遮挡大片| 亚洲国产综合在线看不卡| 性久久久久久久| 欧美三区免费完整视频在线观看| 伊人一区二区三区久久精品| 香蕉成人啪国产精品视频综合网| 欧美国产日韩精品| 欧美在线观看视频| 国产精品免费福利| 亚洲狼人综合| 米奇777超碰欧美日韩亚洲| 亚洲一区视频在线| 最近看过的日韩成人| 久久久久国产一区二区三区| 国产精品中文在线| 亚洲视频国产视频| 欧美大片在线观看| 久久久久**毛片大全| 国产日韩欧美在线看| 香蕉久久一区二区不卡无毒影院| 亚洲欧洲一区二区三区久久| 狂野欧美一区| 伊人成人开心激情综合网| 久久不见久久见免费视频1| 亚洲视频在线一区| 欧美日韩一区三区四区| 日韩视频免费观看| 亚洲国产日韩欧美在线图片| 久久综合久久美利坚合众国| 国产主播一区二区三区| 欧美在线观看视频一区二区三区| 一本高清dvd不卡在线观看| 欧美国产日本| 亚洲精品日韩激情在线电影| 欧美激情一区二区| 蜜桃久久av一区| 亚洲国产日韩欧美| 免费观看亚洲视频大全| 久久网站热最新地址| 在线播放豆国产99亚洲| 蜜桃精品一区二区三区 | 老司机精品导航| 一区二区三区在线免费视频| 久久综合伊人77777尤物| 久久久国产成人精品| 国产在线观看精品一区二区三区| 久久经典综合| 久久激情久久| 影音先锋亚洲电影| 欧美激情91| 欧美激情久久久久久| 一区二区激情| 中文在线一区| 国产深夜精品| 麻豆精品网站| 蜜桃av久久久亚洲精品| 亚洲最新视频在线| 99热免费精品| 国产久一道中文一区| 久久免费99精品久久久久久| 久久一本综合频道| 91久久久久久国产精品| 亚洲三级观看| 国产精品成人一区二区艾草| 欧美一区亚洲二区| 久久久国产精彩视频美女艺术照福利| 亚洲国产va精品久久久不卡综合| 亚洲国产高潮在线观看| 欧美色区777第一页| 久久国产加勒比精品无码| 另类天堂av| 正在播放欧美一区| 午夜精品网站| 亚洲日韩第九十九页|