ACE是个好东东,可是真正用的时候,我还是忍不住重新造车轮。做网络层的时候,考虑了一下还是自己做吧(并不是我觉得ACE不好或不方便,只是因为自己ACE不精,怕乱舞伤了自己)。
从好多方面来说,我都是c++的铁杆,自己给自己的评价是对c++有较深入的认识。不过因为闲时太喜欢做coding游戏,为了节省时间快速见效,投奔锐利c(c#,嘿嘿)了。扯远了,当用c#造车轮的时候,我想到了尽可能使用原生api的c++完成端口可能会对我有帮助,于是简单写了几行。
class thethe
{
public:
bool Init();
private:
HANDLE m_hiocp; //完成端口句柄
HANDLE m_hworker; //工作者线程
static DWORD WINAPI toworker(LPVOID lpthis);
SOCKET m_listener; //服务器监听socket
};
bool thethe::Init()
{
//创建服务器监听socket,必不可少的。
sockaddr_in listeneraddr;
listeneraddr.sin_addr.s_addr = INADDR_ANY;
listeneraddr.sin_family = AF_INET;
listeneraddr.sin_port = htons(2350);
m_listener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
bind(m_listener,(sockaddr*)&listeneraddr,sizeof(sockaddr_in));
listen(m_listener,5);
//1.创建完成端口句柄
m_hiocp = CreateIoCompletionPort(NULL,NULL,NULL,0);
//2.创建工作者线程
m_hworker = CreateThread(NULL,NULL,toworker,(LPVOID)this,0,NULL);
while(true)
{
SOCKET client;
//4.主线程循环使用accept处理新的连接
client = accept(m_listener,(sockaddr*)&listeneraddr,sizeof(listeneraddr));
//6.主线程阻塞等待下一次连接
perIoInfo* pif = new perIoInfo;
pif->sock = client;
//5.直接把新的连接关联到完成端口上,然后投放接受请求,直接返回
CreateIoCompletionPort(client,m_hiocp,pif,0);
myoverlapped* pov = new myoverlapped;
pov->wbuf.buf = pov->buf;
pov->wbuf.len = BUF_SIZE;
DWORD flag;
//投放请求
WSARecv(client,pov->buf,&pov->wbuf.buf,1,&flag,pov,NULL);
//7.操作系统在操作完成后通知完成端口
}
}
DWORD WINAPI thethe::toworker( LPVOID lpthis )
{
thethe* pthis = static_cast<thethe*>(lpthis);
bool bgqcsRet; //GetQueuedCompletionStatus结果
DWORD nofbytes; //做为GetQueuedCompletionStatus的lpNumberOfBytesTransferred参数使用
perIoInfo comKey; //用以满足GQCS函数的第三个参数
LPOVERLAPPED stoverlapped; //满足GQCS第四个参数
while (true)
{
//3.工作者线程使用阻塞模式等待完成端口信号
bgqcsRet = GetQueuedCompletionStatus(pthis->m_hiocp,&nofbytes,&comKey,&stoverlapped,INFINITE);
//8.取回刚才投放的消息
//9.处理完毕后投放新的处理请求(一般是投放新的读请求)
}
return 0;
}
为了把事情最简单化,我让完成端口只处理接收问题,实际使用中,listener也可以绑定在完成端口上,在完成工作者线程中处理接受新的连接工作。
值得说明的是,GetQueuedCompletionStatus的第三个参数lpCompletionKey和CreateIoCompletionPort的第三个参数CompletionKey是同一类型,其实是在CreateIoCompletionPort中我们传入,操作系统在暂时代我们保管,在GetQueuedCompletionStatus函数中把这个参数返回。也就是说,这个参数是和句柄关联的(这里的句柄也就是socket了)。
既然说到CompletionKey了,那就再说说GetQueuedCompletionStatus的lpOverlapped参数。对于句柄sock而已,它可以产生的信号有:accept、recv、send(close是一种特殊的recv)几种,对应的函数AcceptEx、WSARecv、WSASend都有lpOverlapped参数,秘诀就在这里,GetQueuedCompletionStatus取出了我们关心的所有信息(我们无非关心句柄、数据两部分的信息)。