100分求教不够再加:GetQueuedCompletionStatus返回出错的的疑惑

时间:2022-12-16 21:29:42
我正在看别人的代码,其中一段(工作者线程)如下:
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)
{
    这里整段的代码不太理解,各位高手帮忙详细解释一下啊 !!!
}
拜托了,刚开始看完成端口,郁闷了一下午了!!!

10 个解决方案

#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


我是楼主,看上面的问题啊 

#6


得到s(SOCKET类型)就是为了调用 WSAGetOverlappedResult取检测完成状态。当accept的时候,检测监听(Listen)Socket,其它情况检查通讯(Communication)Socket。更多的信息建议查看 MSDN.

其实这个写法不是必要的,只是个人的一种检测错误源或者相关socket状态的写法或手段。

#7


僵哥,可以在问几个小问题吗?

1.完成端口完全可以在前一个io未完成前投递多个io操作,比如WSASend,   
  io投递后只不过缩投递的缓冲区会被系统锁定,这也就需要很大的内存,之所以说它比重叠io多占用系统资源 
锁定谁的缓冲区?

2.完成例程、完成端口提供了比较好的解决方案,只不过对于完成例程   
  来说,初次使用的朋友可能感觉不是很好用,其实它也可以实现很高的性能解决方案,因为他本身就是通过用户APC来
实现;最后一个是完成端口通知,相对于完成例程来说,它建立多工作线程来监视多个socket事件更加容易。   
这个完成端口和完成例程不是一个概念吗?

#8


引用 7 楼 zhangyanli 的回复:
僵哥,可以在问几个小问题吗? 

1.完成端口完全可以在前一个io未完成前投递多个io操作,比如WSASend,    
  io投递后只不过缩投递的缓冲区会被系统锁定,这也就需要很大的内存,之所以说它比重叠io多占用系统资源  
锁定谁的缓冲区? 

2.完成例程、完成端口提供了比较好的解决方案,只不过对于完成例程    
  来说,初次使用的朋友可能感觉不是很好用,其实它也可以实现很高的性能解决方案,因为他本身就是通过用户APC来 
实现;最后一个是完成端口通知,相对于完成例程来说,它建立多工作线程来监视多个socket事件更加容易。    
这个完成端口和完成例程不是一个概念吗?

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

#9


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

#10


up

#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


我是楼主,看上面的问题啊 

#6


得到s(SOCKET类型)就是为了调用 WSAGetOverlappedResult取检测完成状态。当accept的时候,检测监听(Listen)Socket,其它情况检查通讯(Communication)Socket。更多的信息建议查看 MSDN.

其实这个写法不是必要的,只是个人的一种检测错误源或者相关socket状态的写法或手段。

#7


僵哥,可以在问几个小问题吗?

1.完成端口完全可以在前一个io未完成前投递多个io操作,比如WSASend,   
  io投递后只不过缩投递的缓冲区会被系统锁定,这也就需要很大的内存,之所以说它比重叠io多占用系统资源 
锁定谁的缓冲区?

2.完成例程、完成端口提供了比较好的解决方案,只不过对于完成例程   
  来说,初次使用的朋友可能感觉不是很好用,其实它也可以实现很高的性能解决方案,因为他本身就是通过用户APC来
实现;最后一个是完成端口通知,相对于完成例程来说,它建立多工作线程来监视多个socket事件更加容易。   
这个完成端口和完成例程不是一个概念吗?

#8


引用 7 楼 zhangyanli 的回复:
僵哥,可以在问几个小问题吗? 

1.完成端口完全可以在前一个io未完成前投递多个io操作,比如WSASend,    
  io投递后只不过缩投递的缓冲区会被系统锁定,这也就需要很大的内存,之所以说它比重叠io多占用系统资源  
锁定谁的缓冲区? 

2.完成例程、完成端口提供了比较好的解决方案,只不过对于完成例程    
  来说,初次使用的朋友可能感觉不是很好用,其实它也可以实现很高的性能解决方案,因为他本身就是通过用户APC来 
实现;最后一个是完成端口通知,相对于完成例程来说,它建立多工作线程来监视多个socket事件更加容易。    
这个完成端口和完成例程不是一个概念吗?

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

#9


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

#10


up