iocp的简单实现

本文借助炽离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

等待acceptsocket

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绑定,获取AcceptExGetAcceptSockAddrs函数指针 启动工作线程,投递accept操作

private

UINT    StartThreadPull()

获取计算机cpu数目,并创建启动工作线程

private

Bool GetLPFNAcceptEXAndGetAcceptSockAddrs()

获取AcceptExGetAcceptSockAddrs函数指针

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();

工作线程函数

三:代码实现

       1. CSingleton.h
 
#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();
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IOCP(Input/Output Completion Port)是一种高效的异步I/O模型,可用于实现高性能的服务器。在Windows系统中,可以使用IOCP API来创建一个IOCP服务器。 下面是一个简单IOCP服务器的示例代码: ```c #include <stdio.h> #include <winsock2.h> #include <windows.h> #define PORT 12345 #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 typedef struct { OVERLAPPED overlapped; SOCKET socket; WSABUF wsaBuf; CHAR buffer[BUFFER_SIZE]; DWORD bytesTransferred; DWORD flags; } PER_IO_DATA, *LPPER_IO_DATA; VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD dwBytesTransfered, LPOVERLAPPED lpOverlapped, DWORD dwFlags); int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); // 创建监听套接字 SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if (listenSocket == INVALID_SOCKET) { printf("Error creating listen socket: %d\n", WSAGetLastError()); return 1; } // 绑定端口号 SOCKADDR_IN serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(PORT); if (bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { printf("Error binding port: %d\n", WSAGetLastError()); return 1; } // 监听连接 if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { printf("Error listening: %d\n", WSAGetLastError()); return 1; } // 创建IOCP句柄 HANDLE iocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (iocpHandle == NULL) { printf("Error creating IOCP handle: %d\n", GetLastError()); return 1; } // 将监听套接字绑定到IOCP句柄上 if (CreateIoCompletionPort((HANDLE)listenSocket, iocpHandle, (ULONG_PTR)listenSocket, 0) == NULL) { printf("Error binding listen socket to IOCP handle: %d\n", GetLastError()); return 1; } // 创建客户端套接字 SOCKET clientSockets[MAX_CLIENTS]; for (int i = 0; i < MAX_CLIENTS; i++) { clientSockets[i] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if (clientSockets[i] == INVALID_SOCKET) { printf("Error creating client socket: %d\n", WSAGetLastError()); return 1; } } // 接受连接 for (;;) { SOCKADDR_IN clientAddr; int clientAddrLen = sizeof(clientAddr); SOCKET clientSocket = accept(listenSocket, (SOCKADDR*)&clientAddr, &clientAddrLen); if (clientSocket == INVALID_SOCKET) { printf("Error accepting connection: %d\n", WSAGetLastError()); continue; } // 将客户端套接字绑定到IOCP句柄上 if (CreateIoCompletionPort((HANDLE)clientSocket, iocpHandle, (ULONG_PTR)clientSocket, 0) == NULL) { printf("Error binding client socket to IOCP handle: %d\n", GetLastError()); closesocket(clientSocket); continue; } // 分配PER_IO_DATA结构体 LPPER_IO_DATA perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA)); perIoData->socket = clientSocket; perIoData->wsaBuf.buf = perIoData->buffer; perIoData->wsaBuf.len = BUFFER_SIZE; perIoData->flags = 0; // 开始接收数据 DWORD bytesRecv; if (WSARecv(clientSocket, &perIoData->wsaBuf, 1, &bytesRecv, &perIoData->flags, &perIoData->overlapped, CompletionRoutine) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { printf("Error starting receive: %d\n", WSAGetLastError()); GlobalFree(perIoData); closesocket(clientSocket); continue; } } } // 关闭监听套接字 closesocket(listenSocket); // 释放资源 for (int i = 0; i < MAX_CLIENTS; i++) { closesocket(clientSockets[i]); } WSACleanup(); return 0; } VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD dwBytesTransfered, LPOVERLAPPED lpOverlapped, DWORD dwFlags) { LPPER_IO_DATA perIoData = (LPPER_IO_DATA)lpOverlapped; if (dwErrorCode == 0 && dwBytesTransfered > 0) { printf("Received %d bytes from socket %d\n", dwBytesTransfered, perIoData->socket); } else { printf("Error receiving data: %d\n", dwErrorCode); } GlobalFree(perIoData); closesocket(perIoData->socket); } ``` 在这个示例中,我们首先创建一个监听套接字,绑定端口号并开始监听连接。然后,我们创建一个IOCP句柄,并将监听套接字绑定到IOCP句柄上。 接下来,我们创建一些客户端套接字,并在接受连接时将它们绑定到IOCP句柄上。每当有一个客户端连接到服务器时,我们为其分配一个PER_IO_DATA结构体,并使用WSARecv函数开始接收数据。在WSARecv函数中,我们将PER_IO_DATA结构体的地址传递给了overlapped参数,这样就可以在数据接收完成后得到它。 当数据接收完成后,系统会调用CompletionRoutine回调函数。在这个函数中,我们可以检查数据接收是否成功,并释放PER_IO_DATA结构体和客户端套接字。 需要注意的是,在IOCP服务器中,所有的I/O操作都是异步的,因此我们需要使用OVERLAPPED结构体来描述每一个I/O操作,并在每个I/O操作完成后启动回调函数。在这个示例中,我们使用了PER_IO_DATA结构体来扩展OVERLAPPED结构体,并包含了一些额外的信息,例如套接字、接收缓冲区等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值