本文藉助熾離的winsock編程IOCP模型實現代碼進行進一步分析以及擴展。
本文主要使用到了以下幾個知識點,1.標準模板庫(stl)使用 2.多線程的使用3.單例等設計模式4.socket網絡通訊5.面向對象的編程思想,適用於初學iocp的同學。
一:簡單的iocp,我們主要分爲以下幾個類。大家不要急着看代碼,先可以通過以下的模塊試着編寫以下
類 | 描述 |
CThreadLockCs | 防止同一時間,多條線程對同一操作,同時執行。 |
CSingleton<T> | 單例模式,這是一個模板類,利用單例模式生成唯一對象。 |
COverlappedIOInfo | 對Overlapped進行封裝,主要作用是將自己所需要的數據進行綁定 |
CIOCP | 對iocp進行封裝 |
CTaskService | 線程服務,用於線程的管理。 |
CServer | 服務器核心,創建socket,投遞操作,處理完成消息等。 |
二:各類的成員簡介
類名:CThreadLockCs | 繼承: | 父類: | |
成員 | |||
權限 | 變量名 | 描述 | |
private | CRITICAL_SHCTLON m_cs | 臨界區對象 | |
public | CThreadLockCs() | 構造函數,初始化臨界區對象 | |
public | ~CThreadLockCs() | 析構函數,釋放臨界區對象 | |
public | Void lock() | 臨界區入口 | |
public | Void unlock | 臨界區出口 |
類名:CSingleton<T> | 繼承: | 父類: | |
成員 | |||
權限 | 變量名 | 描述 | |
private | Static T* _Instance | 利用本類生成的對象 | |
private | CSingleton | 構造函數,對成員初始化 | |
private | Static CThreadLockCs Ics | 臨界區對象 | |
public | Static T* Instance | 生成對象 | |
public | Void Close() | 釋放對象 |
類名:COverlappedIOinfo | 繼承:public | 父類:Overlapped | |
成員 | |||
權限 | 變量名 | 描述 | |
public | SOCKET m_sScost | 用於通訊的套接字 | |
public | WSABUF m_recvBuf | 接收緩存區 | |
public | Char m_crecvBuf | 接收緩存區 | |
public | WSABUF m_sendBuf | 發送緩存區 | |
public | Char m_csendBuf | 發送緩存區 | |
public | Sockaddr m_addr | 對端地址 | |
public | COverlappedIOinfo() | 構造函數初始化套接字和復位 | |
public | ~COverlappedIOinfo() | 析構函數關閉套接字 | |
public | VoidResetOverlapped() | 復位Overlapped | |
public | VoidResetRecvBuffer() | 復位RecvBuffer | |
public | Void ResetSendBuffed() | 復位SendBuffer |
類名:CIOCP | 繼承: | 父類: | |
成員 | |||
權限 | 變量名 | 描述 | |
private | HANDLE m_hIOCP | 完成端口句柄 | |
public | CIOCP(int nMaxConcurrency=-1) | 構造函數,初始化完成端口句柄,並創建完成端口。 | |
public | ~CIOCP() | 析構函數,關閉完成端口。 | |
public | BoolCreateIOCP(int nMaxConcurrency = 0); | 創建完成端口,並指定最大併發線程數量。 | |
public | Bool CloseIOCP() | 關閉完成端口 | |
public | bool AsscciateDevice(HANDLE hDevice,ULONG_PTR CompKey) | 爲設備關聯一個完成端口 | |
public | bool AsscciateScoket(SOCKET hSocket,ULONG_PTR CompKey); | 將套接字關聯一個完成端口 | |
public | bool PostStatus(ULONG_PTR CompKey,DWORD dwNumBytes = 0,OVERLAPPED* po =NULL); | 添加一個完成端口io操作 | |
public | bool GetStatus(ULONG_PTR* pCompKey, PDWORD pdwNumBytes,OVERLAPPED** ppo,DWORD dwMilliseconds =INFINITE); | 從完成隊列中獲取io通知 | |
public | constHANDLE GetIOCP(); | 獲取iocp對象 |
類名:CTaskService | 繼承: | 父類: | |
成員 | |||
權限 | 變量名 | 描述 | |
private | staticUINT WorkThread(LPVOID param); | 工作線程訪問接口 | |
private | std::vector<CWinThread*> vec_threads; | 線程隊列 | |
public | UINT Activate(int num = 1); | 用於激活一定數量的工作線程 | |
public | UINT GetThreadsNum(void); | 獲取線程數目 | |
protected | CTaskService(void); | 構造函數,只能用於子類 | |
protected | ~CTaskService(void); | 析構函數 | |
protected | virtual void svc(); | 子類對線程函數進行重寫 | |
protected | VoidClose(); | 退出線程,由子類決定 |
類名:Cserver | 繼承:public | 父類:CTaskService | |
成員 | |||
權限 | 變量名 | 描述 | |
private | WSAData m_wsaData | winsock版本類型 | |
private | SOCKET m_sListen | 監聽socket | |
private | std::vector<SOCKET> m_vecAcps | 等待accept的socket | |
private | std::vector<COverlappedIOInfo*> m_vecContInfo; | 已建立連接的信息 | |
private | CThreadLockCs m_lsc; | 互斥同步 | |
private | CIOCP m_iocp; | Icop封裝 | |
private | LPFN_ACCEPTEX m_lpfnAcceptEx; | AcceptEx函數指針 | |
private | LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptSockAddrs | GetAcceptSockAddrs函數指針 | |
public | Cserver() | 初始化函數指針獲取版本類型 | |
public | ~Cserver() | 對套接字 線程等資源釋放 並終止Winsock 2 DLL (Ws2_32.dll)的使用 | |
public | bool StartListen(unsigned short port,std::string ip) | 創建監聽socket,創建完成端口並將完成端口與監聽socket綁定,獲取AcceptEx與GetAcceptSockAddrs函數指針 啓動工作線程,投遞accept操作 | |
private | UINT StartThreadPull() | 獲取計算機cpu數目,並創建啓動工作線程 | |
private | Bool GetLPFNAcceptEXAndGetAcceptSockAddrs() | 獲取AcceptEx和GetAcceptSockAddrs函數指針 | |
private | Bool PostAccept(COverlappedIOInfo* ol) | 創建一個連接socket,設置COverlappedIOInfo,並開始進行事件監聽(投遞accept) | |
private | Bool DoAccept(COverlappedIOInfo* ol, DWORD NumberOfBytes = 0) |
處理accept請求 | |
private | bool PostRecv(COverlappedIOInfo* ol) | 投遞recv請求 | |
private | bool DoRecv(COverlappedIOInfo* ol) | 處理recv請求 | |
private | bool DeleteLink(SOCKET s) | 從已連接socket列表中移除socket及釋放空間 | |
private | void CloseServer() | 對套接字 線程等資源釋放 | |
protected | virtual void svc(); | 工作線程函數 |
三:代碼實現
#ifndef CSINGLETON_H
#define CSINGLETON_H
#pragma once
//互斥訪問鎖
class CThreadLockCs
{
public:
//此函數初始化一個臨界區對象。
CThreadLockCs() { InitializeCriticalSection(&m_cs); }
//刪除臨界區對象
~CThreadLockCs() { DeleteCriticalSection(&m_cs); }
//加鎖接下來的代碼處理過程不允許其他線程同時操作
void lock() { EnterCriticalSection(&m_cs); }
//解鎖解鎖 到EnterCriticalSection之間代碼資源已經釋放了,其他線程可以進行操作
void unlock() { LeaveCriticalSection(&m_cs); }
private:
//臨界區對象
CRITICAL_SECTION m_cs;
};
/************************************************************************
singleton模式類模板
1:延遲創建類實例 2:double check 3:互斥訪問 4:模板
************************************************************************/
template<class T>
class CSingleton
{
private:
static T* _instance;
CSingleton(void);
static CThreadLockCs lcs;
public:
static T* Instance(void);
static void Close(void);
};
//模板類static變量
template<class T>
T* CSingleton<T>::_instance = NULL;
template<class T>
CThreadLockCs CSingleton<T>::lcs;
//模板類方法實現
template<class T>
CSingleton<T>::CSingleton(void)
{
}
template<class T>
T* CSingleton<T>::Instance(void)
{
//double-check
//延遲創建,只有調用方訪問Instance纔會創建類實例
if (_instance == NULL)
{
//互斥訪問鎖,用CriticalSection實現
lcs.lock();
if (_instance == NULL)
{
_instance = new T;
}
lcs.unlock();
}
return _instance;
}
template<class T>
void CSingleton<T>::Close(void)
{
if (_instance)
{
delete _instance;
}
}
#endif
2.OverlappedIOInfo.h#ifndef OVERLAPPEDIOINFO_H
#define OVERLAPPEDIOINFO_H
#pragma once
#include <WinSock2.h>
#include <MSWSock.h>
#define MAXBUF 1024*8
/******************************************************************************
Module: OverlappedIOInfo.h
Notices: Copyright (c) 20161201 whg
Purpose:
IOCP網絡編程模型中,需要用到GetQueuedCompletionStatus函數獲取已完成事件。
但該函數的返回參數無socket或buffer的描述信息。
一個簡單的解決辦法,創建一個新的結構,該結構第一個參數是OVERLAPPED。
由於AcceptEx、WSASend等重疊IO操作傳入的是Overlapped結構體的地址,調用AcceptEx等重疊IO操作,
在Overlapped結構體後面開闢新的空間,寫入socket或buffer的信息,即可將socket或buffer的信息由
GetQueuedCompletionStatus帶回。
參考《windows核心編程》和CSDN PiggyXP
******************************************************************************/
enum IOOperType {
TYPE_ACP, //accept事件到達,有新連接請求
TYPE_RECV, //數據接收事件
TYPE_SEND, //數據發送事件
TYPE_CLOSE, //關閉事件
TYPE_NO_OPER
};
class COverlappedIOInfo:public OVERLAPPED
{
public:
SOCKET m_sSocket; //套接字
WSABUF m_recvBuf; //接收緩衝區,用於AcceptEx、WSARecv操作
char m_cRecvBuf[MAXBUF];
WSABUF m_sendBuf; //發送緩衝區,用於WSASend操作
char m_cSendBuf[MAXBUF];
sockaddr_in m_addr; //對端地址
public:
COverlappedIOInfo();
~COverlappedIOInfo();
//復位Overlapped
void ResetOverlapped();
//復位RecvBuffer
void ResetRecvBuffer();
//復位SendBuffer
void ResetSendBuffer();
};
#endif // !OVERLAPPEDIOINFO_H
3.OverlappedIOInfo.cpp#include "stdafx.h"
#include "OverlappedIOInfo.h"
COverlappedIOInfo::COverlappedIOInfo()
{
m_sSocket = INVALID_SOCKET;
ResetOverlapped();
ResetRecvBuffer();
ResetSendBuffer();
}
COverlappedIOInfo::~COverlappedIOInfo()
{
if (m_sSocket != INVALID_SOCKET)
{
closesocket(m_sSocket);
m_sSocket = INVALID_SOCKET;
}
}
void COverlappedIOInfo::ResetOverlapped()
{
Internal = InternalHigh = 0;
Offset = OffsetHigh = 0;
hEvent = NULL;
}
void COverlappedIOInfo::ResetRecvBuffer()
{
ZeroMemory(m_cRecvBuf, MAXBUF);
m_recvBuf.buf = m_cRecvBuf;
m_recvBuf.len = MAXBUF;
}
void COverlappedIOInfo::ResetSendBuffer()
{
ZeroMemory(m_cSendBuf, MAXBUF);
m_sendBuf.buf = m_cSendBuf;
m_sendBuf.len = MAXBUF;
}
4.CIOCP.h#ifndef CIOCP_H
#define CIOCP_H
#include <winsock2.h>
#include <MSWSock.h>
/******************************************************************************
Module: IOCP.h
Notices: Copyright (c) 2007 Jeffrey Richter & Christophe Nasarre
Purpose: This class wraps an I/O Completion Port.
Revise: IOCP封裝類,由《windows核心編程》第10章示例程序源碼改編所得
******************************************************************************/
#pragma once
class CIOCP
{
private:
HANDLE m_hIOCP; //IOCP句柄
public:
CIOCP(int nMaxConcurrency = -1);
~CIOCP();
//創建IOCP,nMaxConcurrency指定最大線程併發數量,0默認爲cpu數量
bool CreateIOCP(int nMaxConcurrency = 0);
//關閉IOCP
bool CloseIOCP();
//爲設備關聯一個IOCP
bool AsscciateDevice(HANDLE hDevice, ULONG_PTR CompKey);
//爲socket關聯一個IOCP
bool AsscciateScoket(SOCKET hSocket, ULONG_PTR CompKey);
//爲iocp傳遞事件通知
bool PostStatus(ULONG_PTR CompKey, DWORD dwNumBytes = 0, OVERLAPPED* po = NULL);
//從IO完成隊列中獲取事件通知。IO完成隊列無事件時,該函數將阻塞
bool GetStatus(ULONG_PTR* pCompKey, PDWORD pdwNumBytes, OVERLAPPED** ppo, DWORD dwMilliseconds = INFINITE);
//獲取IOCP對象
const HANDLE GetIOCP();
};
5.CIOCP.cpp#endif // !CIOCP_H
#include "stdafx.h"
#include "CIOCP.h"
#ifdef _DEBUG
#define ASSERT(T) assert(T)
#else
#define ASSERT(T) (T)
#endif
CIOCP::CIOCP(int nMaxConcurrency)
{
m_hIOCP = NULL;
if (-1 != nMaxConcurrency)
{
CreateIOCP(nMaxConcurrency);
}
}
CIOCP::~CIOCP()
{
if (m_hIOCP != NULL)
ASSERT(CloseHandle(m_hIOCP));
}
//創建IOCP,nMaxConcurrency指定最大線程併發數量,0默認爲cpu數量
bool CIOCP::CreateIOCP(int nMaxConcurrency )
{
//創建一個完成端口
m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, nMaxConcurrency);
//效驗
ASSERT(m_hIOCP != NULL);
return (m_hIOCP != NULL);
}
//關閉IOCP
bool CIOCP::CloseIOCP()
{
//關閉完成端口
bool bResult = CloseHandle(m_hIOCP);
m_hIOCP = NULL;
return(bResult);
}
//爲設備關聯一個IOCP
bool CIOCP::AsscciateDevice(HANDLE hDevice, ULONG_PTR CompKey)
{ //關聯完成端口
//1關聯的設備句柄2完成端口句柄3需要綁定的結構體
bool fOk = (CreateIoCompletionPort(hDevice, m_hIOCP, CompKey, 0) == m_hIOCP);
//效驗
ASSERT(fOk);
return(fOk);
}
//爲socket關聯一個IOCP
bool CIOCP::AsscciateScoket(SOCKET hSocket, ULONG_PTR CompKey)
{
return AsscciateDevice((HANDLE)hSocket, CompKey);
}
//爲iocp傳遞事件通知
bool CIOCP::PostStatus(ULONG_PTR CompKey, DWORD dwNumBytes , OVERLAPPED* po)
{
//手動添加一個完成端口io操作
bool fOk = PostQueuedCompletionStatus(m_hIOCP, dwNumBytes, CompKey, po);
ASSERT(fOk);
return(fOk);
}
//從IO完成隊列中獲取事件通知。IO完成隊列無事件時,該函數將阻塞
bool CIOCP::GetStatus(ULONG_PTR* pCompKey, PDWORD pdwNumBytes, OVERLAPPED** ppo, DWORD dwMilliseconds)
{
//監控完成端口
//1 我們創建的完成端口 2操作完成後返回的字節數 3需要綁定的結構體
//4重疊結構LPOVERLAPPED 5等待完成端口的超時時間
return(GetQueuedCompletionStatus(m_hIOCP, pdwNumBytes, pCompKey, ppo, dwMilliseconds));
}
//獲取IOCP對象
const HANDLE CIOCP::GetIOCP()
{
return m_hIOCP;
}
///////////////////////////////// End of File /////////////////////////////////
6.TaskService.h#ifndef WHG_CTASKSVC
#define WHG_CTASKSVC
#include <vector>
#include <afxwin.h>
class CTaskService
{
public:
//Activate用於激活一定數量的工作者線程,默認激活數量爲1。返回當前線程隊列大小
UINT Activate(int num = 1);
//獲取線程隊列大小
UINT GetThreadsNum(void);
protected:
//只有子類纔可以構造父類,拒絕外部訪問構造類實例
CTaskService(void);
~CTaskService(void);
//子類應重定義工作線程細節
virtual void svc();
//Close用於等待線程結束並關閉線程,退出線程由子類控制
void Close();
private:
//工作者線程訪問接口
static UINT WorkThread(LPVOID param);
//線程隊列
std::vector<CWinThread*> vec_threads;
};
#endif
7.TaskService.cpp#include "stdafx.h"
#include "TaskService.h"
CTaskService::CTaskService(void)
{
}
CTaskService::~CTaskService(void)
{
Close();
}
UINT CTaskService::Activate(int num)
{
for (int i = 0; i < num; i++)
{
CWinThread* pwt = AfxBeginThread(WorkThread, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
if (pwt)
{
pwt->m_bAutoDelete = false;
pwt->ResumeThread();
vec_threads.push_back(pwt);
}
}
return vec_threads.size();
}
UINT CTaskService::GetThreadsNum(void)
{
return vec_threads.size();
}
UINT CTaskService::WorkThread(LPVOID param)
{
CTaskService* pts = (CTaskService*)param;
if (pts)
{
pts->svc();
}
return 0;
}
void CTaskService::svc()
{
}
void CTaskService::Close()
{
int cnt = vec_threads.size();
if (cnt > 0)
{
std::vector<CWinThread*>::iterator iter = vec_threads.begin();
for (; iter != vec_threads.end(); iter++)
{
CWinThread* pwt = *iter;
WaitForSingleObject(pwt->m_hThread, INFINITE);
delete pwt;
}
vec_threads.clear();
}
}
8.Server.h#ifndef SERVER_H
#define SERVER_H
#pragma once
#include "TaskService.h"
#include "OverlappedIOInfo.h"
#include "CSingleton.h"
#include "CIOCP.h"
class CServer :public CTaskService
{
#define ACCEPT_SOCKET_NUM 10
private:
WSAData m_wsaData; //winsock版本類型
SOCKET m_sListen; //端口監聽套接字
std::vector<SOCKET> m_vecAcps; //等待accept的套接字
//已建立連接的信息,每個結構含有一個套接字、發送緩衝和接收緩衝,以及對端地址
std::vector<COverlappedIOInfo*> m_vecContInfo;
//操作vector的互斥訪問鎖
CThreadLockCs m_lsc;
//IOCP封裝類
CIOCP m_iocp;
//AcceptEx函數指針
LPFN_ACCEPTEX m_lpfnAcceptEx;
//GetAcceptSockAddrs函數指針
LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptSockAddrs;
public:
CServer(void);
~CServer(void);
bool StartListen(unsigned short port, std::string ip);
protected:
virtual void svc();
private:
//啓動CPU*2個線程,返回已啓動線程個數
UINT StartThreadPull();
//獲取AcceptEx和GetAcceptExSockaddrs函數指針
bool GetLPFNAcceptEXAndGetAcceptSockAddrs();
//利用AcceptEx監聽accept請求
bool PostAccept(COverlappedIOInfo* ol);
//處理accept請求,NumberOfBytes=0表示沒有收到第一幀數據,>0表示收到第一幀數據
bool DoAccept(COverlappedIOInfo* ol, DWORD NumberOfBytes = 0);
//投遞recv請求
bool PostRecv(COverlappedIOInfo* ol);
//處理recv請求
bool DoRecv(COverlappedIOInfo* ol);
//從已連接socket列表中移除socket及釋放空間
bool DeleteLink(SOCKET s);
//釋放3個部分步驟:
//1:清空IOCP線程隊列,退出線程
//2: 清空等待accept的套接字m_vecAcps
//3: 清空已連接的套接字m_vecContInfo並清空緩存
void CloseServer();
};
typedef CSingleton<CServer> SERVER;
#endif
9.Server.cpp#include "stdafx.h"
#include "Server.h"
CServer::CServer()
{
m_lpfnAcceptEx = NULL;
m_lpfnGetAcceptSockAddrs = NULL;
WSAStartup(MAKEWORD(2, 2), &m_wsaData);
printf("%d\n", m_wsaData.iMaxSockets);
}
CServer::~CServer()
{
CloseServer();
WSACleanup();
}
bool CServer::StartListen(unsigned short port, std::string ip)
{
//listen socket需要將accept操作投遞到完成端口,因此,listen socket屬性必須有重疊IO
m_sListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (m_sListen == INVALID_SOCKET)
{
cout << "WSASocket create socket error" << endl;
return false;
}
//創建並設置IOCP併發線程數量
if (m_iocp.CreateIOCP() == FALSE)
{
cout << "IOCP create error,error code " << WSAGetLastError() << endl;
return false;
}
//將listen socket綁定至iocp
if (!m_iocp.AsscciateScoket(m_sListen, TYPE_ACP))
{
cout << "iocp Associate listen Socket error" << endl;
return false;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons(port);
if (ip.empty())
{
service.sin_addr.s_addr = INADDR_ANY;
}
else
{
service.sin_addr.s_addr = inet_addr(ip.c_str());
}
if (bind(m_sListen, (sockaddr*)&service, sizeof(service)) == SOCKET_ERROR)
{
cout << "bind() error,error code " << WSAGetLastError() << endl;
return false;
}
cout << "bind ok!" << endl;
if (listen(m_sListen, SOMAXCONN) == SOCKET_ERROR)
{
cout << "listen() error,error code " << WSAGetLastError() << endl;
return false;
}
cout << "listen ok!" << endl;
//啓動工作者線程
int threadnum = StartThreadPull();
cout << "啓動工作者線程,num=" << threadnum << endl;
//獲取AcceptEx和GetAcceptSockAddrs函數指針
if (!GetLPFNAcceptEXAndGetAcceptSockAddrs())
{
return false;
}
//創建10個acceptex
for (int i = 0; i < ACCEPT_SOCKET_NUM; i++)
{
//用accept
COverlappedIOInfo* ol = new COverlappedIOInfo;
if (!PostAccept(ol))
{
delete ol;
return false;
}
}
}
void CServer::svc()
{
while (true)
{
DWORD NumberOfBytes = 0;
unsigned long CompletionKey = 0;
OVERLAPPED* ol = NULL;
if (FALSE != GetQueuedCompletionStatus(m_iocp.GetIOCP(), &NumberOfBytes, &CompletionKey, &ol, WSA_INFINITE))
{
COverlappedIOInfo* olinfo = (COverlappedIOInfo*)ol;
if (CompletionKey == TYPE_CLOSE)
{
break;
}
if (NumberOfBytes == 0 && (CompletionKey == TYPE_RECV || CompletionKey == TYPE_SEND))
{
//客戶端斷開連接
cout << "客戶端斷開連接,ip=" << inet_ntoa(olinfo->m_addr.sin_addr) << ",port=" << olinfo->m_addr.sin_port << endl;
DeleteLink(olinfo->m_sSocket);
continue;
}
switch (CompletionKey)
{
case TYPE_ACP:
{
DoAccept(olinfo, NumberOfBytes);
PostAccept(olinfo);
}
break;
case TYPE_RECV:
{
DoRecv(olinfo);
PostRecv(olinfo);
}
break;
case TYPE_SEND:
{
}
break;
default:
break;
}
}
else
{
int res = WSAGetLastError();
switch (res)
{
case ERROR_NETNAME_DELETED:
{
COverlappedIOInfo* olinfo = (COverlappedIOInfo*)ol;
if (olinfo)
{
cout << "客戶端異常退出,ip=" << inet_ntoa(olinfo->m_addr.sin_addr) << ",port=" << olinfo->m_addr.sin_port << endl;
DeleteLink(olinfo->m_sSocket);
}
}
break;
default:
cout << "workthread GetQueuedCompletionStatus error,error code " << WSAGetLastError() << endl;
break;
}
continue;
}
}
cout << "workthread stop" << endl;
}
//啓動CPU*2個線程,返回已啓動線程個數
UINT CServer::StartThreadPull()
{
//獲取系統cpu個數啓動線程
SYSTEM_INFO si;
GetSystemInfo(&si);
//啓動cpu數量*2個線程
return Activate(si.dwNumberOfProcessors * 2);
}
//獲取AcceptEx和GetAcceptExSockaddrs函數指針
bool CServer::GetLPFNAcceptEXAndGetAcceptSockAddrs()
{
DWORD BytesReturned = 0;
//獲取AcceptEx函數指針
GUID GuidAcceptEx = WSAID_ACCEPTEX;
if (SOCKET_ERROR == WSAIoctl(
m_sListen,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&m_lpfnAcceptEx,
sizeof(m_lpfnAcceptEx),
&BytesReturned,
NULL, NULL))
{
cout << "WSAIoctl get AcceptEx function error,error code " << WSAGetLastError() << endl;
return false;
}
//獲取GetAcceptexSockAddrs函數指針
GUID GuidGetAcceptexSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
if (SOCKET_ERROR == WSAIoctl(
m_sListen,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidGetAcceptexSockAddrs,
sizeof(GuidGetAcceptexSockAddrs),
&m_lpfnGetAcceptSockAddrs,
sizeof(m_lpfnGetAcceptSockAddrs),
&BytesReturned,
NULL, NULL))
{
cout << "WSAIoctl get GetAcceptexSockAddrs function error,error code " << WSAGetLastError() << endl;
return false;
}
return true;
}
//利用AcceptEx監聽accept請求
bool CServer::PostAccept(COverlappedIOInfo* ol)
{
if (m_lpfnAcceptEx == NULL)
{
cout << "m_lpfnAcceptEx is NULL" << endl;
return false;
}
SOCKET s = ol->m_sSocket;
ol->ResetRecvBuffer();
ol->ResetOverlapped();
ol->ResetSendBuffer();
ol->m_sSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (ol->m_sSocket == INVALID_SOCKET)
{
cout << "WSASocket error ,error code " << WSAGetLastError() << endl;
return false;
}
//這裏建立的socket用來和對端建立連接,終會加入m_vecContInfo列表
//調用acceptex將accept socket綁定至完成端口,並開始進行事件監聽
//這裏需要傳遞Overlapped,new一個COverlappedIOInfo
//AcceptEx是m_listen的監聽事件,m_listen已經綁定了完成端口;雖然ol->m_sSock已經創建,
//但未使用,現在不必爲ol->m_sSock綁定完成端口。在AcceptEx事件發生後,再爲ol->m_sSock綁定IOCP
DWORD byteReceived = 0;
if (FALSE == m_lpfnAcceptEx(
m_sListen,
ol->m_sSocket,
ol->m_recvBuf.buf,
ol->m_recvBuf.len - (sizeof(SOCKADDR_IN) + 16) * 2,
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
&byteReceived,
ol))
{
DWORD res = WSAGetLastError();
if (ERROR_IO_PENDING != res)
{
cout << "AcceptEx error , error code " << res << endl;
return false;
}
}
std::vector<SOCKET>::iterator iter = m_vecAcps.begin();
for (; iter != m_vecAcps.end(); iter++)
{
if (*iter == s)
{
*iter = ol->m_sSocket;
}
}
if (iter == m_vecAcps.end())
{
m_vecAcps.push_back(ol->m_sSocket);
}
return true;
}
//處理accept請求,NumberOfBytes=0表示沒有收到第一幀數據,>0表示收到第一幀數據
bool CServer::DoAccept(COverlappedIOInfo* ol, DWORD NumberOfBytes )
{
//分支用於獲取遠端地址。
//如果接收TYPE_ACP同時收到第一幀數據,則第一幀數據內包含遠端地址。
//如果沒有收到第一幀數據,則通過getpeername獲取遠端地址
SOCKADDR_IN* ClientAddr = NULL;
int remoteLen = sizeof(SOCKADDR_IN);
if (NumberOfBytes > 0)
{
//接受的數據分成3部分,第1部分是客戶端發來的數據,第2部分是本地地址,第3部分是遠端地址。
if (m_lpfnGetAcceptSockAddrs)
{
SOCKADDR_IN* LocalAddr = NULL;
int localLen = sizeof(SOCKADDR_IN);
m_lpfnGetAcceptSockAddrs(
ol->m_recvBuf.buf,
ol->m_recvBuf.len - (sizeof(SOCKADDR_IN) + 16) * 2,
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
(LPSOCKADDR*)&LocalAddr,
&localLen,
(LPSOCKADDR*)&ClientAddr,
&remoteLen);
cout << "收到新的連接請求,ip=" << inet_ntoa(ClientAddr->sin_addr) << ",port=" << ClientAddr->sin_port <<
"數據爲:" << ol->m_recvBuf.buf << endl;
}
}
else if (NumberOfBytes == 0)
{
//未收到第一幀數據
if (SOCKET_ERROR == getpeername(ol->m_sSocket, (sockaddr*)ClientAddr, &remoteLen))
{
cout << "getpeername error,error code " << WSAGetLastError() << endl;
}
else
{
cout << "收到新的連接請求,ip=" << inet_ntoa(ClientAddr->sin_addr) << ",port=" << ClientAddr->sin_port << endl;
}
}
COverlappedIOInfo* pol = new COverlappedIOInfo;
pol->m_sSocket = ol->m_sSocket;
pol->m_addr = *ClientAddr;
//服務端只收取recv,同時監聽recv和send可用設計位偏移,用或運算實現
if (m_iocp.AsscciateScoket(pol->m_sSocket, TYPE_RECV))
{
PostRecv(pol);
m_vecContInfo.push_back(pol);
}
else
{
delete pol;
return false;
}
return true;
}
//投遞recv請求
bool CServer::PostRecv(COverlappedIOInfo* ol)
{
DWORD BytesRecvd = 0;
DWORD dwFlags = 0;
ol->ResetOverlapped();
ol->ResetRecvBuffer();
int recvnum = WSARecv(ol->m_sSocket, &ol->m_recvBuf, 1, &BytesRecvd, &dwFlags, (OVERLAPPED*)ol, NULL);
if (recvnum != 0)
{
int res = WSAGetLastError();
if (WSA_IO_PENDING != res)
{
cout << "WSARecv error,error code " << res << endl;
}
}
return true;
}
//處理recv請求
bool CServer::DoRecv(COverlappedIOInfo* ol)
{
cout << "收到客戶端數據:ip=" << inet_ntoa(ol->m_addr.sin_addr) << ",port=" << ol->m_addr.sin_port <<
";內容=" << ol->m_recvBuf.buf << endl;
return true;
}
//從已連接socket列表中移除socket及釋放空間
bool CServer::DeleteLink(SOCKET s)
{
m_lsc.lock();
std::vector<COverlappedIOInfo*>::iterator iter = m_vecContInfo.begin();
for (; iter != m_vecContInfo.end(); iter++)
{
if (s == (*iter)->m_sSocket)
{
COverlappedIOInfo* ol = *iter;
closesocket(s);
m_vecContInfo.erase(iter);
delete ol;
break;
}
}
m_lsc.unlock();
return true;
}
//釋放3個部分步驟:
//1:清空IOCP線程隊列,退出線程
//2: 清空等待accept的套接字m_vecAcps
//3: 清空已連接的套接字m_vecContInfo並清空緩存
void CServer::CloseServer()
{
//1:清空IOCP線程隊列,退出線程,有多少個線程發送多少個PostQueuedCompletionStatus信息
int threadnum = GetThreadsNum();
for (int i = 0; i < threadnum; i++)
{
if (FALSE == m_iocp.PostStatus(TYPE_CLOSE))
{
cout << "PostQueuedCompletionStatus error,error code " << WSAGetLastError() << endl;
}
}
//2:清空等待accept的套接字m_vecAcps
std::vector<SOCKET>::iterator iter = m_vecAcps.begin();
for (; iter != m_vecAcps.end(); iter++)
{
SOCKET s = *iter;
closesocket(s);
}
m_vecAcps.clear();
//3:清空已連接的套接字m_vecContInfo並清空緩存
std::vector<COverlappedIOInfo*>::iterator iter2 = m_vecContInfo.begin();
for (; iter2 != m_vecContInfo.end(); iter2++)
{
COverlappedIOInfo* ol = *iter2;
closesocket(ol->m_sSocket);
iter2 = m_vecContInfo.erase(iter2);
delete ol;
}
m_vecContInfo.clear();
}