结构原理如下:
1.首先 完成端口的特性:
异步 非阻塞 效率较高
2.原理:
预先创建好n个空闲的Waiter套接字
然后把这些套接字交给监听套接字管理
再将监听套接字交给IOCP完成端口处理
创建好我们的线程池
如果有n客户端链接我们的服务器
我们就新创建出n个空闲的线程 来维持空闲的线程数量平衡
然后向内核投递接受数据请求
将相应的Waiter交给IOCP管理
完整代码如下
类声明
#ifndef CTCPMAP_H
#define CTCPMAP_H
#include<list>
#include<winsock2.h>
#include<windows.h>
#include<iostream>
#include <Mswsock.h>
#include<map>
using namespace std;
#define MaxNum 64
#define NT_ACCEPT 0
#define NT_READ 1
struct DataInfo
{
char* pszbuf;
int nPacksize;
int noffest;
};
struct MySocket
{
OVERLAPPED olp;//用来发通知
SOCKET socket;//套接字waiter
char szbufp[1024];//接收缓冲区
int type;//判断网络类型
};
class CTCPmap
{
public:
//构造
CTCPmap();
//析构
~CTCPmap();
//初始化网络
bool InitnewWork();
//销毁网络
void DeleteWork();
//线程函数
static DWORD WINAPI ThreadEventSelect(LPVOID lpvoid);
static DWORD WINAPI ThreadIOCP(LPVOID lpvoid);
static DWORD WINAPI ThreadSelect(LPVOID lpvoid);
static DWORD WINAPI ThreadAccept(LPVOID lpvoid);
static DWORD WINAPI ThreadRecv(LPVOID lpvoid);
//发送数据
bool sendDate(SOCKET sockWaiter,char*szbuf,int nLen);
//接收数据
void recvDate();
bool PostAccept();
bool PostRecv(MySocket*);
private:
//监听者句柄
SOCKET m_soclisten;
//总线程池
std::list<HANDLE> my_Threadpool;
//服务者线程池
std::list<SOCKET> my_ThreadWaiter;
//我的套接字链表
std::list<MySocket*> my_mysocketWaiter;
//线程结束标识
bool my_FlagQuit;
//传服务者sockWaiter用到的容器
std::map<DWORD,SOCKET> my_map;
//结构体与套接字的映射
std::map<SOCKET,DataInfo*> m_sdmap;
fd_set m_fd;
SOCKET m_arySocket[MaxNum];
HANDLE m_aryEvent[MaxNum];
int m_nEventNum;
HANDLE m_hIOCP;
};
#endif // CTCPMAP_H
实现:
#include "ctcpmap.h"
CTCPmap::CTCPmap()
{
m_soclisten=0;
my_FlagQuit=true;
FD_ZERO(&m_fd);
m_nEventNum=0;
ZeroMemory(m_arySocket,sizeof(m_arySocket));
ZeroMemory(m_aryEvent,sizeof(m_aryEvent));
m_hIOCP=0;
}
CTCPmap::~CTCPmap()
{
}
bool CTCPmap::InitnewWork()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
DeleteWork();
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return false;
}
//创建套接字
m_soclisten=socket(AF_INET,SOCK_STREAM,0);
if(m_soclisten==INVALID_SOCKET)
{
DeleteWork();
return false;
}
//绑定
sockaddr_in addrserver;
//绑定IP
addrserver.sin_addr.S_un.S_addr=0;
addrserver.sin_port=htons(8899);
addrserver.sin_family=AF_INET;
if(SOCKET_ERROR==bind(m_soclisten,(sockaddr*)&addrserver,sizeof(addrserver)))
{
DeleteWork();
return false;
}
//监听
if(SOCKET_ERROR==listen(m_soclisten,10))
{
DeleteWork();
return false;
}
//1.创建IOCP
m_hIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,NULL);
//2.将listen 交给IOCP
CreateIoCompletionPort((HANDLE)m_soclisten,m_hIOCP,m_soclisten,0);
//3.创建空闲Waiter
SYSTEM_INFO si;
GetSystemInfo(&si);
for(int i=0;i <si.dwNumberOfProcessors;i++)
{
if(!PostAccept())i--;
}
//4.创建线程池
for(int i=0;i <si.dwNumberOfProcessors;i++)
{
HANDLE hand=CreateThread(0,0,&ThreadIOCP,this,0,0);
if(hand)
my_Threadpool.push_back(hand);
}
// HANDLE hevent=WSACreateEvent();
// if(!WSAEventSelect(m_soclisten,hevent,FD_ACCEPT))
// {
// m_arySocket[m_nEventNum]=m_soclisten;
// m_aryEvent[m_nEventNum]=hevent;
// m_nEventNum++;
// }
// HANDLE hand=CreateThread(0,0,&ThreadEventSelect,this,0,0);
// if(hand)
// my_Threadpool.push_back(hand);
// //将套接字加入到集合
// FD_SET(m_soclisten,&m_fd);
// //创建线程
// HANDLE hand=CreateThread(0,0,&ThreadSelect,this,0,0);
// if(hand)
// my_Threadpool.push_back(hand);
// HANDLE hand=CreateThread(0,0,&ThreadAccept,this,0,0);
// if(hand)
// my_Threadpool.push_back(hand);
// hand=CreateThread(0,0,&ThreadRecv,this,0,0);
// //创建服务者线程 加入到线程池
// if(hand)
// my_Threadpool.push_back(hand);
return true;
}
void CTCPmap::DeleteWork()
{
//线程销毁
my_FlagQuit=false;
auto ite=my_Threadpool.begin();
while(ite!=my_Threadpool.end())
{
if(WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
TerminateThread(*ite,-1);
CloseHandle(*ite);
*ite=NULL;
ite++;
}
my_Threadpool.clear();
//关闭句柄
if(m_soclisten)
{
closesocket(m_soclisten);
m_soclisten=0;
}
//卸载服务器
WSACleanup();
}
DWORD WINAPI CTCPmap::ThreadEventSelect(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
DWORD dwIndex;
WSANETWORKEVENTS we;
sockaddr_in addrclient;
int nsize=sizeof(addrclient);
int nRecvNum;
while(pthis->my_FlagQuit)
{
//等事件
dwIndex=WSAWaitForMultipleEvents(pthis->m_nEventNum,pthis->m_aryEvent,FALSE,WSA_INFINITE,0);
dwIndex-=WSA_WAIT_EVENT_0;
//判断发生什么事
if(SOCKET_ERROR==WSAEnumNetworkEvents(pthis->m_arySocket[dwIndex],
pthis->m_aryEvent[dwIndex],
&we))
continue;
//处理
if(we.lNetworkEvents&FD_ACCEPT)
{
cout<<"等待客户端连接"<<endl;
SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);
cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;
//对应失败 跳出 继续循环
if(sockWaiter==INVALID_SOCKET)continue;
HANDLE hevent=WSACreateEvent();//人工的无信号事件
if(!WSAEventSelect(sockWaiter,hevent,FD_READ|FD_CLOSE))
{
pthis->m_arySocket[pthis->m_nEventNum]=sockWaiter;
pthis->m_aryEvent[pthis->m_nEventNum]=hevent;
pthis->m_nEventNum++;
}
}
if(we.lNetworkEvents&FD_READ)
{
DataInfo *pData=pthis->m_sdmap[pthis->m_arySocket[dwIndex]];
//代表waiter 有数据来了
if(!pData)
{
pData=new DataInfo;
pData->noffest=0;
pData->nPacksize=0;
pthis->m_sdmap[pthis->m_arySocket[dwIndex]]=pData;
//waiter come
nRecvNum=recv(pthis->m_arySocket[dwIndex],(char*)&pData->nPacksize,sizeof(int),0);
if(pData->nPacksize>0)
{
pData->pszbuf=new char[pData->nPacksize];
}
}
else
{
nRecvNum=recv(pthis->m_arySocket[dwIndex],pData->pszbuf+pData->noffest,pData->nPacksize,0);
if(nRecvNum>0)
{
pData->noffest+=nRecvNum;
pData->nPacksize-=nRecvNum;
if(pData->nPacksize==0)
{
//数据包接受完整
cout<<pData->pszbuf<<endl;
delete []pData->pszbuf;
pData->pszbuf=NULL;
delete [] pData;
pData=NULL;
pthis->m_sdmap[pthis->m_arySocket[dwIndex]]=NULL;
}
}
}
}
if(we.lNetworkEvents&FD_CLOSE)
{
closesocket(pthis->m_arySocket[dwIndex]);
WSACloseEvent(pthis->m_aryEvent[dwIndex]);
if(pthis->m_nEventNum>1)
{
pthis->m_arySocket[dwIndex]=pthis->m_arySocket[pthis->m_nEventNum-1];
pthis->m_aryEvent[dwIndex]=pthis->m_aryEvent[pthis->m_nEventNum-1];
}
pthis->m_nEventNum--;
}
}
return 0;
}
DWORD CTCPmap::ThreadIOCP(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
DWORD dwNumberOfByTesTransferred;
SOCKET sock;
MySocket *pSocket;
BOOL bflag;
while(pthis->my_FlagQuit)
{
bflag=GetQueuedCompletionStatus(pthis->m_hIOCP,
&dwNumberOfByTesTransferred,
(PULONG_PTR)&sock,
(LPOVERLAPPED*)&pSocket,
INFINITE);
if(!bflag)
{
continue;
}
if(!sock&&!pSocket)
continue;
switch(pSocket->type)
{
case NT_ACCEPT:
pthis->PostAccept();
pthis->PostRecv(pSocket);
CreateIoCompletionPort((HANDLE)pSocket->socket,pthis->m_hIOCP,pSocket->socket,0);
break;
case NT_READ:
cout<<pSocket->szbufp<<endl;
pthis->PostRecv(pSocket);
break;
}
}
}
//将集合交给select线程函数
DWORD WINAPI CTCPmap::ThreadSelect(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
fd_set fdtemp;
TIMEVAL tv;
tv.tv_sec=0;
tv.tv_usec=100;
sockaddr_in addrclient;
int nRecvNum;
int nsize=sizeof(addrclient);
while(pthis->my_FlagQuit)
{
fdtemp =pthis->m_fd;
//将套接字交给集合
select(0,&fdtemp,0,0,&tv);
if(FD_ISSET(pthis->m_soclisten,&fdtemp))
{
//代表有人连接我了
SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);
cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;
//对应失败 跳出 继续循环
if(sockWaiter==INVALID_SOCKET)continue;
//将waiter加入到集合内
FD_SET(sockWaiter,&pthis->m_fd);
}
//校验
for(unsigned int i=1;i<pthis->m_fd.fd_count;i++)
{
if(FD_ISSET(pthis->m_fd.fd_array[i],&fdtemp))
{
DataInfo *pData=pthis->m_sdmap[pthis->m_fd.fd_array[i]];
//代表waiter 有数据来了
if(!pData)
{
pData=new DataInfo;
pData->noffest=0;
pData->nPacksize=0;
pthis->m_sdmap[pthis->m_fd.fd_array[i]]=pData;
//waiter come
nRecvNum=recv(pthis->m_fd.fd_array[i],(char*)&pData->nPacksize,sizeof(int),0);
if(pData->nPacksize>0)
{
pData->pszbuf=new char[pData->nPacksize];
}
}
else
{
nRecvNum=recv(pthis->m_fd.fd_array[i],pData->pszbuf+pData->noffest,pData->nPacksize,0);
if(nRecvNum>0)
{
pData->noffest+=nRecvNum;
pData->nPacksize-=nRecvNum;
if(pData->nPacksize==0)
{
//数据包接受完整
cout<<pData->pszbuf<<endl;
delete []pData->pszbuf;
pData->pszbuf=NULL;
delete [] pData;
pData=NULL;
pthis->m_sdmap[pthis->m_fd.fd_array[i]]=NULL;
}
}
}
}
}
}
return 0;
}
//接收客户端的线程函数
DWORD WINAPI CTCPmap::ThreadAccept(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
sockaddr_in addrclient;
int nsize=sizeof(addrclient);
u_long iMode=1;
while(pthis->my_FlagQuit)
{
//店长接受客人 分配给服务员 --接受链接--accept();
//服务者与他的客人对应起来
SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);
cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;
//对应失败 跳出 继续循环
if(sockWaiter==INVALID_SOCKET)continue;
//对应成功 线程池添加了一个服务者线程
//将Waiter 介入到链表 将waiter设置为非阻塞
ioctlsocket(sockWaiter,FIONBIO,&iMode);
//将服务者套接字加入到集合
//FD_SET(sockWaiter,&(pthis->m_fd));
pthis->my_ThreadWaiter.push_back(sockWaiter);
}
return 0;
}
DWORD WINAPI CTCPmap::ThreadRecv(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
while(pthis->my_FlagQuit)
{
// 接受
pthis->recvDate();
Sleep(10);
}
return 0;
}
void CTCPmap::recvDate()//接收函数
{
// 服务员等客人说话 --recv();
char *pszbuf=NULL;
//当前位置标识
int offset=0;
//包长度
int nPackSize;
int nRecvNum;
SOCKET sockWaiter;
auto ite=my_ThreadWaiter.begin();
while(ite!=my_ThreadWaiter.end())
{
sockWaiter=*ite;
//jieshoubaodaxiao
nRecvNum=recv(sockWaiter,(char*)&nPackSize,sizeof(int ),0);
if(nRecvNum<=0)
{
//判断服务器是否下线
if(WSAGetLastError()==10054)
{
//删除映射
auto ite=my_map.begin();
while(ite!=my_map.end())
{
if(ite->second==sockWaiter)
{
my_map.erase(ite);
closesocket(sockWaiter);
break;
}
//ite++;
}
break;
}
continue;
ite++;
}
//获得接受内容
pszbuf=new char[nPackSize];
while(nPackSize)//看当前剩余的还有多少没接受完
{
nRecvNum=recv(sockWaiter,pszbuf+offset,nPackSize,0);
if(nRecvNum>0)
{
nPackSize-=nRecvNum;
offset+=nRecvNum;
}
}
cout<<"client say:"<<pszbuf<<endl;
sendDate(sockWaiter,pszbuf, nRecvNum);
delete []pszbuf;
pszbuf=NULL;
ite++;
}
}
bool CTCPmap::PostAccept()
{
MySocket* pSocket=new MySocket;
pSocket->olp.hEvent=WSACreateEvent();
pSocket->type=NT_ACCEPT;
pSocket->socket=socket(AF_INET,SOCK_STREAM,0);
if(!AcceptEx(m_soclisten,pSocket->socket,pSocket->szbufp,0,sizeof(sockaddr)+16,sizeof(sockaddr)+16,0,&pSocket->olp))
{
if(WSAGetLastError()!=ERROR_IO_PENDING)
{
WSACloseEvent(pSocket->olp.hEvent);
closesocket(pSocket->socket);
delete pSocket;
pSocket=NULL;
return false;
}
}
my_mysocketWaiter.push_back(pSocket);
return true;
}
bool CTCPmap::PostRecv(MySocket *psocket)
{
DWORD dwNumberOfBytesRecvd;
DWORD dwFlag=0;
psocket->type=NT_READ;
WSABUF wb;
wb.buf=psocket->szbufp;
wb.len=sizeof(psocket->szbufp);
if(WSARecv(psocket->socket,&wb,1,&dwNumberOfBytesRecvd,&dwFlag,&psocket->olp,0))
{
if(WSAGetLastError()!=WSA_IO_PENDING)
return false;
}
return true;
}
bool CTCPmap:: sendDate(SOCKET sockWaiter,char*szbuf,int nLen)
{
if(sockWaiter==INVALID_SOCKET||!szbuf||nLen<=0)
return false;
if(send(sockWaiter,(char*)&nLen,sizeof(int ),0)<=0)
return false;
if(send(sockWaiter,szbuf,nLen,0)<=0)
return false;
int nRecvNum;
int nRecvNuumSIze=sizeof(szbuf);
while(my_FlagQuit)
{
cin>>szbuf;
send(sockWaiter,(char*)&nRecvNuumSIze,sizeof(int),0);
send(sockWaiter,szbuf,sizeof(szbuf),0);
//recv()
nRecvNum= recv(sockWaiter,szbuf,sizeof(szbuf),0);
if(sockWaiter>0)
{
cout<<"fuwuqi say:"<<szbuf<<endl;
}
}
return true;
}
主函数:
#include <QCoreApplication>
#include<winsock2.h>
#include<iostream>
#include"ctcpmap.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/*
*1.选择种类 --WSAStartup();
*2.雇店长 --创建套接字 socket();
*3.找地 --绑定 --bind();
*4.店长宣传 --监听 --listen();
*5.店长接受客人 分配给服务员 --接受链接--accept();
*6.客人与服务员 服务员等客人说话 --recv();
*7.回复 --send();
*8.下班--closesocket();
*9.关门--WASCleanup();
*
*/
CTCPmap ctcp;
if(ctcp.InitnewWork())
cout<<"initnework sucess"<<endl;
return a.exec();
}