完成端口GetQueuedCompletionStatus返回值的问题

来源:互联网 发布:淘宝店铺转让在哪里 编辑:程序博客网 时间:2024/05/20 04:51

 完成端口GetQueuedCompletionStatus返回值的问题

先看看GetQueuedCompletionStatus函数的完整声明:
BOOL GetQueuedCompletionStatus(
  HANDLE CompletionPort,      
  LPDWORD lpNumberOfBytes,   
  PULONG_PTR lpCompletionKey, 
  LPOVERLAPPED *lpOverlapped, 
  DWORD dwMilliseconds
);

再看看MSDN上对其返回值的说明:

If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped parameters.
如果函数从完成端口取出一个成功I/O操作的完成包,返回值为非0。函数在指向lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped的参数中存储相关信息。


If *lpOverlapped is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the lpNumberOfBytesTransferred and lpCompletionKey parameters. To get extended error information, call GetLastError. If the function did not dequeue a completion packet because the wait timed out, GetLastError returns WAIT_TIMEOUT.
如果 *lpOverlapped为空并且函数没有从完成端口取出完成包,返回值则为0。函数则不会在lpNumberOfBytes and lpCompletionKey所指向的参数中存储信息。调用GetLastError可以得到一个扩展错误信息。如果函数由于等待超时而未能出列完成包,GetLastError返回WAIT_TIMEOUT. 

If *lpOverlapped is not NULL and the function dequeues a completion packet for a failed I/O operation from the completion port, the return value is zero. The function stores information in the variables pointed to by lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped. To get extended error information, call GetLastError
如果 *lpOverlapped不为空并且函数从完成端口出列一个失败I/O操作的完成包,返回值为0。函数在指向lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped的参数指针中存储相关信息。调用GetLastError可以得到扩展错误信息


If a socket handle associated with a completion port is closed, GetQueuedCompletionStatus returns ERROR_SUCCESS, with lpNumberOfBytes equal zero. 
如果关联到一个完成端口的一个socket句柄被关闭了,则GetQueuedCompletionStatus返回ERROR_SUCCESS,并且lpNumberOfBytes等于0

相应的,我处理的方式:
while(){

flag=GetQueuedCompletionStatus( 
  HANDLE CompletionPort,      
  LPDWORD lpNumberOfBytes,   
  PULONG_PTR lpCompletionKey, 
  LPOVERLAPPED *lpOverlapped, 
  DWORD dwMilliseconds
);

针对:如果 *lpOverlapped为空并且函数没有从完成端口取出完成包,返回值则为0。函数则不会在lpNumberOfBytes and lpCompletionKey所指向的参数中存储信息。

if(lpOverlapped== NULL)  {continue;}  

针对: 如果 *lpOverlapped不为空并且函数从完成端口出列一个失败I/O操作的完成包,返回值为0。函数在指向lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped的参数指针中存储相关信息。调用GetLastError可以得到扩展错误信息
if(FALSE == flag && GetLastError()!=0){ 归还IO句柄;continue;}

针对:如果关联到一个完成端口的一个socket句柄被关闭了,则GetQueuedCompletionStatus返回ERROR_SUCCESS,并且lpNumberOfBytes等于0 .

ERROR_SUCCESS这个值是0,即返回值是0(false)且GetLastError()等于0,与上面的错误处理代码是有分别的。
if(opType != IO_ACCEPT && dwByteTrans == 0) {做断开处理,归还IO;continue;}

针对: 如果函数从完成端口取出一个成功I/O操作的完成包,返回值为非0。函数在指向lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped的参数中存储相关信息。
switch(){根据IO类型做相关操作}

}

现在的问题是:我需要在(FALSE == flag && GetLastError()!=0)的情况下做一些处理。导致GetQueuedCompletionStatus返回失败的因素有很多,最常见的是〖1236〗-由本地系统终止网络连接,〖1234〗-没有任何服务正在远程系统上的目标网络终结点上操作,〖64〗-指定的网络名不再可用等等。之前发生错误时简单的回还IO句柄,但有些问题由此而产生。


1.WSASEND/WSARECV调用成功,但实际上IO被重置,GetQueuedCompletionStatus返回错误。

2.服务器主动关闭了socket,导致投递的io返回。
3.由其他原因导致io失败等。如udp等待接收的socket会发生997错误:重叠 I/O 操作在进行中。(这个也不好理解,为什么接收在进行却返回?应该阻塞等待其完成返回啊)

之前没想到udp等待接收的IO会失败返回,于是未加区分的归还IO了事。(面向终端的tcp的io请求失败后,由于终端重连时会继续投递接收请求,因此面向终端的一边是没有问题的)导致后期udp socket没有接收请求投递而收不到任何数据。因此需要在这个错误处理过程中区分IO类型分别做处理。这里复杂的原因在于该完成端口绑定了tcp socket,udp socket,等待连接的socket,主动连接的socket等等这些类型,用途各异的socket。

这样,需要在udp请求IO失败后主动的重新投递接收请求,主动地向服务器发送。在此过程中需要取完成键,一个奇怪的现象产生了,此时的完成键lpCompletionKey有时候为NULL。按照MS的说法:如果 *lpOverlapped不为空并且函数从完成端口出列一个失败I/O操作的完成包,返回值为0。函数在指向lpNumberOfBytesTransferred, lpCompletionKey, lpOverlapped的参数指针中存储相关信息。如果*lpOverlapped为空,则参数不赋值。而我在前一种情况下的完成键却是NULL,并未赋值。之前没考虑,从中取值导致程序时常崩溃,现在加入判断则恢复正常。

      

 但是为什么完成键在IO句柄不为空的情况下没有赋值?这个问题依然不解,而且对我的系统而言仍然有隐忧。我是在完成键赋值的情况下会重新投递面向IAG的udp接收请求,若完成键没有赋值,投递io没有进行,后期的数据会收不到。这种情况虽然没有发生,但是是有可能的。


原创粉丝点击