目录
IOCP(Input/Output Completion Ports)是Windows操作系统提供的一种高性能、可扩展的异步I/O模型。
-
定义:
-
IOCP是Input/Output Completion Ports的简称,中文翻译为“输入输出完成端口”。
-
它是一个支持多个同时发生的异步I/O操作的应用程序编程接口(API)。
-
-
原理:
-
IOCP通过事件驱动的方式来处理I/O请求,避免了传统的阻塞式I/O操作带来的性能问题。
-
主要包含三个组件:I/O端口、完成端口和工作者线程池。
-
应用程序首先创建一个或多个I/O端口,并将它们关联到套接字或文件句柄上。
-
当应用程序需要进行I/O操作时,它调用系统级别的API,将请求提交到I/O端口上。
-
操作系统内核将I/O请求与相应的I/O端口关联,并立即返回,使应用程序可以继续执行其他操作。
-
内核在后台异步地执行I/O操作,并将结果存储在完成队列中。
-
当I/O操作完成时,内核会通知完成端口,并将完成信息添加到完成队列中。
-
应用程序通过调用GetQueuedCompletionStatus()函数获取完成队列中的已完成请求,并按需处理它们。
-
-
实现细节:
-
使用CreateIoCompletionPort函数来创建或关联I/O端口。
-
在CreateIoCompletionPort函数中,可以指定一个最大允许并发的线程数量上限。
-
IOCP对象内部有一个先进先出(FIFO)队列,用于存放IOCP所关联的输入输出端的服务请求完成消息。
-
多个线程负责从IOCP消息队列中取走完成通知并执行数据处理;如果队列中没有消息,那么线程阻塞挂起在该队列。
-
-
应用场景:
-
网络编程:IOCP可以帮助网络应用程序实现高并发、低延迟的数据传输,适合处理大量连接和大量数据流的场景,如网络游戏、在线聊天、视频流媒体等。
-
数据库操作:加速数据库操作中的文件读写、网络传输等I/O操作,提升数据库系统的性能和稳定性。
-
高性能服务器开发:作为一种高效的事件驱动模型,帮助服务器应用程序快速响应客户端请求,提高系统的吞吐量和并发度。
-
多线程编程:帮助开发者实现多线程编程中的任务分配和负载均衡,提高代码执行效率和并行度。
-
-
总结:
-
IOCP是一种高效的异步I/O模型,它通过事件驱动的方式和完成队列来处理I/O请求,从而避免了阻塞式I/O带来的性能问题。
-
在需要高性能、高并发、低延迟的应用场景下,IOCP具有很大的优势。
-
-
代码:
TCPServer.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <windows.h>
#include <map>
#include <mswsock.h>
#define ONEPAGE 4096
#define NT_ACCEPT 0
#define NT_READ 1
struct MySocket{
OVERLAPPED m_olp; //发送通知的 事件
SOCKET m_sock; //waiter
char m_szBuffer[ONEPAGE];// 接受数据的缓冲区
int m_nNetType; //网络信息的类型
};
class TCPServer
{
public:
TCPServer();
~TCPServer();
public:
//1.初始化网络--加载、创建socket\bind\listen
bool initNetWork(const char* szip = "127.0.0.1",unsigned short nport = 1234);
void unInitNetWork(const char* szerr = "null"); //卸载网络
bool sendData(SOCKET sockWaiter,const char* szbuf,int nlen); //向指定客户端发送数据
void recvData(); //接收数据
static DWORD WINAPI threadProc(LPVOID lpvoid);
public:
bool postAccept();
bool postRecv(MySocket*);
private:
SOCKET m_socklisten;
std::list<HANDLE> m_lstThread;
std::list<MySocket*> m_lstSocket;
bool m_bFlagQuit;
std::map<DWORD,SOCKET> m_mapThreadIdToSocket;
HANDLE m_hIOCP;
};
#endif // TCPSERVER_H
TCPServer.cpp
#include "tcpserver.h"
TCPServer::TCPServer()
{
m_socklisten = 0;
m_bFlagQuit = true;
}
TCPServer::~TCPServer()
{
}
bool TCPServer::initNetWork(const char *szip, unsigned short nport)
{
//1.选择种类 韩餐 火锅 串串香 川菜 -- 加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return false;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
unInitNetWork();
return false;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//2.雇人-店长--
m_socklisten = socket(AF_INET,SOCK_STREAM,0);
if(m_socklisten == INVALID_SOCKET){
unInitNetWork("socket err");
return false;
}
//3.选择地址--
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_addr.S_un.S_addr = inet_addr(szip);
addrserver.sin_port = htons(nport);
if( SOCKET_ERROR == bind(m_socklisten,(sockaddr*)&addrserver,sizeof(addrserver))){
unInitNetWork("bind err");
return false;
}
//4.宣传--
if( SOCKET_ERROR == listen(m_socklisten,1000)){ // 1_1_1_1______
unInitNetWork("listen err");
return false;
}
//1.创建IOCP
m_hIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE,NULL,NULL,NULL);
//2.将listen交给IOCP管理
CreateIoCompletionPort((HANDLE)m_socklisten,m_hIOCP,m_socklisten,0);
//3.创建一些空闲socket
SYSTEM_INFO si;
GetSystemInfo(&si);
for(DWORD i = 0; i < si.dwNumberOfProcessors*2;i++){
if(postAccept())
continue;
}
//4.创建线程池
for(DWORD i = 0; i < si.dwNumberOfProcessors*2;i++){
//创建接收连接的线程
HANDLE hThread = CreateThread(0,0,&threadProc,this,0,0);
if(hThread)
m_lstThread.push_back(hThread);
}
return true;
}
DWORD TCPServer::threadProc(LPVOID lpvoid)
{
TCPServer *pthis = (TCPServer*)lpvoid;
DWORD dwNumberOfBytesTransferred;
SOCKET sock;
MySocket *psocket;
BOOL bflag;
while(pthis->m_bFlagQuit){
//观察IOCP的状态
bflag = GetQueuedCompletionStatus(pthis->m_hIOCP,
&dwNumberOfBytesTransferred,
(PULONG_PTR)&sock,
(LPOVERLAPPED*)&psocket,
INFINITE
);
if(!bflag)
continue;
if(!sock|| !psocket)
continue;
switch (psocket->m_nNetType) {
case NT_ACCEPT:
{
pthis->postAccept();
pthis->postRecv(psocket);
CreateIoCompletionPort((HANDLE)psocket->m_sock,pthis->m_hIOCP,psocket->m_sock,0);
}
break;
case NT_READ:
printf("client say:%s\n",psocket->m_szBuffer);
pthis->postRecv(psocket);
break;
default:
break;
}
}
return 0;
}
bool TCPServer::postRecv(MySocket *psocket)
{
psocket->m_nNetType = NT_READ;
DWORD dwNumberOfBytesRecvd;
DWORD dwFlags = 0;
WSABUF wb;
wb.buf =psocket->m_szBuffer;
wb.len = sizeof(psocket->m_szBuffer);
if(WSARecv(psocket->m_sock,
&wb,
1,
&dwNumberOfBytesRecvd,
&dwFlags,
&psocket->m_olp,0)){
return false;
}
return true;
}
bool TCPServer::postAccept()
{
DWORD dwBytesReceived;
MySocket *p = new MySocket;
p->m_nNetType = NT_ACCEPT;
p->m_olp.hEvent = WSACreateEvent();
p->m_sock = socket(AF_INET,SOCK_STREAM,0);
if(!AcceptEx(m_socklisten,p->m_sock,p->m_szBuffer,0,
sizeof(sockaddr_in)+16,sizeof(sockaddr_in)+16,
&dwBytesReceived,&p->m_olp)){
if(WSAGetLastError() != ERROR_IO_PENDING){
WSACloseEvent(p->m_olp.hEvent);
closesocket(p->m_sock);
delete p;
return false;
}
}
m_lstSocket.push_back(p);
return true;
}
void TCPServer::unInitNetWork(const char* szerr )
{
printf(szerr);
if(m_socklisten){
closesocket(m_socklisten);
m_socklisten = 0;
}
WSACleanup();
auto itesocket = m_lstSocket.begin();
while(itesocket != m_lstSocket.end()){
closesocket((*itesocket)->m_sock);
WSACloseEvent((*itesocket)->m_olp.hEvent);
delete *itesocket;
itesocket++;
}
m_lstSocket.clear();
//结束所有的线程
int nsize = m_lstThread.size();
while(nsize-->0){
PostQueuedCompletionStatus(m_hIOCP,0,0,0);
}
m_bFlagQuit = false;
auto ite = m_lstThread.begin();
while(ite !=m_lstThread.end()){
//判断线程状态
if(WAIT_TIMEOUT == WaitForSingleObject(*ite,100))
TerminateThread(*ite,-1);
CloseHandle(*ite);
*ite = NULL;
++ite;
}
m_lstThread.clear();
}
bool TCPServer::sendData(SOCKET sockWaiter, const char *szbuf, int nlen)
{
//发送的包大小
if(send(sockWaiter,(const char*)&nlen,sizeof(int),0) <=0)
return false;
//发送包内容
if(send(sockWaiter,szbuf,nlen,0) <=0)
return false;
return true;
}