我正在看别人的代码,其中一段(工作者线程)如下:
DWORD WINAPI CIOCPServer::_WorkerThreadProc(LPVOID lpParam)
{
#ifdef _DEBUG
::OutputDebugString(" WorkerThread 启动... \n");
#endif // _DEBUG CIOCPServer *pThis = (CIOCPServer*)lpParam; CIOCPBuffer *pBuffer;
DWORD dwKey;
DWORD dwTrans;
LPOVERLAPPED lpol;
while(TRUE)
{
// 在关联到此完成端口的所有套节字上等待I/O完成
BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
&dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE); if(dwTrans == -1) // 用户通知退出
{
#ifdef _DEBUG
::OutputDebugString(" WorkerThread 退出 \n");
#endif // _DEBUG
::ExitThread(0);
} pBuffer = CONTAINING_RECORD(lpol, CIOCPBuffer, ol);
int nError = NO_ERROR;
if(!bOK) // 在此套节字上有错误发生
{
SOCKET s;
if(pBuffer->nOperation == OP_ACCEPT)
{
s = pThis->m_sListen;              
}
else
{
if(dwKey == 0)
break;
s = ((CIOCPContext*)dwKey)->s;
}
DWORD dwFlags = 0;
if(!::WSAGetOverlappedResult(s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags))
{
nError = ::WSAGetLastError();
}
}
pThis->HandleIO(dwKey, pBuffer, dwTrans, nError);
}#ifdef _DEBUG
::OutputDebugString(" WorkerThread 退出 \n");
#endif // _DEBUG
return 0;
}其中pBuffer是per-IO数据,HandleIO处理完成的请求,投递新的请求,释放完成的per-I/O和per-handl数据。我的问题是GetQueuedCompletionStatus的返回值不太了解,对以下段代码不太理解:
if(!bOK)
{
    这里整段的代码不太理解,各位高手帮忙详细解释一下啊 !!!
}
拜托了,刚开始看完成端口,郁闷了一下午了!!!

解决方案 »

  1.   

    好象你看的是王艳平那本书的代码,那书上讲的很清楚的,这个代码也有很好的参考价值,你投递IO请求后GetQueuedCompletionStatus回返回他们的完成状态,比如read事件,调用GetQueuedCompletionStatus返回时,它会告诉你收到了多少数据,数据就存放在你提供给他的缓冲区中,提交write操作后,GetQueuedCompletionStatus返回时,它又告诉你已经完成发送了多少数据,返回的发送字节数在dwTrans这个参数里得知,这样你就可以根据per-handl里记录的数据结构判断是否帮你发完提交的数据,没完可以考虑在次提交write(根据你的应用来)
      

  2.   

    你说的太对了,我是看的那本书,不过你说的我似乎都明白,但是我问的是这段代码:
    if(!bOK) 

        这里整段的代码不太理解,各位高手帮忙详细解释一下啊 !!! 
    }
    麻烦你给我解释一下,这里的
    if()
    ......         //1
    else
    ......         //2
    if(!::WSAGetOverlappedResult(s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags)) //3
                {
                    nError = ::WSAGetLastError();
                }
    这三处注释我是在不明白,麻烦您帮我解释一下。
      

  3.   

    还有什么不明白? 1和2就是判断完成的操作,一般有accept(接受连接),read(有数据可读),write(可以写了)这里 我估计你不明白怎么得到CIOCPBuffer这个数据结构,你要注意了,
    CIOCPBuffer的第一个字段就是个overlap结构,也就是CIOCPBuffer这个结构体的首地址,把这个首地址提交,这样在GetQueuedCompletionStatus返回时就能获得CIOCPBuffer表示的结构了,是充分利用这个技巧才能够拓宽表示内容
      

  4.   

    你为什么不看清楚我的问题呢,你说的这些包括这些技巧我还是差不多理解的。我的意思是 我不明白这段代码:
     if(!bOK)                        // 在此套节字上有错误发生
            {
                SOCKET s;
                if(pBuffer->nOperation == OP_ACCEPT)  //1.这里是第一处不明白,为什么这样,得到s做什么
                {
                    s = pThis->m_sListen;              
                }
                else
                {
                    if(dwKey == 0)                    //2.这里又是为什么:1.用意和在;2.为什么又要得到s 
                        break;
                    s = ((CIOCPContext*)dwKey)->s;
                }
                DWORD dwFlags = 0;
                if(!::WSAGetOverlappedResult(s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags))
                {                                     //3.对这个得到的s调用WSAGetOverlappedResult作用。                nError = ::WSAGetLastError();
                }
            }请高手回答我的三个问题
      

  5.   

    得到s(SOCKET类型)就是为了调用WSAGetOverlappedResult取检测完成状态。当accept的时候,检测监听(Listen)Socket,其它情况检查通讯(Communication)Socket。更多的信息建议查看MSDN.其实这个写法不是必要的,只是个人的一种检测错误源或者相关socket状态的写法或手段。
      

  6.   

    僵哥,可以在问几个小问题吗?1.完成端口完全可以在前一个io未完成前投递多个io操作,比如WSASend,   
      io投递后只不过缩投递的缓冲区会被系统锁定,这也就需要很大的内存,之所以说它比重叠io多占用系统资源 
    锁定谁的缓冲区?2.完成例程、完成端口提供了比较好的解决方案,只不过对于完成例程   
      来说,初次使用的朋友可能感觉不是很好用,其实它也可以实现很高的性能解决方案,因为他本身就是通过用户APC来
    实现;最后一个是完成端口通知,相对于完成例程来说,它建立多工作线程来监视多个socket事件更加容易。   
    这个完成端口和完成例程不是一个概念吗?
      

  7.   

    1.锁定的是你WSASend/WSASendTo/WSARecv/WSARecvFrom投递时给的WSABUF结构所指向的内存,其实还有核心内存的占用,而核心内存的过度占用才是影响性能的根本,至于占用系统资源,当出现不足的时候自然也就在应用层体现出来了。
    2.不知道这是谁写的,要不你问问写这段话的人吧。相关重叠I/O的几种应用方式,我已经在你的贴子当中进行了列举,这里就不再重复。
      

  8.   

    需要注意的是重叠I/O是一种I/O模型,相当于一种异步作业。而完成端口只是一种异步作业的完成通知模型。两者完成的是不同的业务内容。只是把他们结合起来可以更方便地实现我们的业务逻辑。对于要求系统主动实现的完成端口通知模型,那么这些操作就依赖于重叠I/O模型,或者说必须基于重叠I/O。