WSARecv()同时发生Overlapped IO投递和立即返回

silon212 2010-03-21 09:41:10
读投递操作如下(只看代码结构):


DWORD PostRead(PerIoOpData* pIoOpData, PerHandleCpKey* pCpKey)
{
// 省略一堆变量声明

// 使用 WSARecv() 投递 Overlapped IO 接收操作

dwErr = WSARecv(pCpKey->sAcpt, &(pIoOpData->wsabuf), 1, &bytesRecvd, &flag,
&(pIoOpData->ol), NULL);

if ( SOCKET_ERROR == dwErr )
{
dwErr = WSAGetLastError();
// 这里为 WSA_IO_PENDING,说明投递 Overlapped IO 成功
}
else
{
// WSARecv() 操作立即完成,此时 bytesRecvd 中保存接收的字节数
_ftprintf(stderr, _T("WSARecv() operation completes immediately\n"));
ProcessRead(pIoOpData, pCpKey, bytesRecvd);
}

dwErr = ERROR_SUCCESS;

RET:
return dwErr;
}


IOCP + 线程池 方式管理交叠IO。

用VC跟踪时,第一个发起投递的线程A,走到这个函数中,调用WSARecv(),然后返回 dwErr = 0,说明“读操作”立即完成,然后执行 _ftprintf() 那句,_ftprintf()还没执行完时,池中的另外一个 worker 线程B 激活了(即GetQueuedCompletionStatus() release 掉了)(我在 worker 线程历程中下了断点,所以能看见这种情况)。

然后就走 线程B 的执行,竟然是读操作的完成通知,那一定是 线程 A 的 WSARecv() 的交叠IO 投递成功了。

然后 线程B 先打印出(我的接收后处理是打印)接收到的数据,然后回到循环中的 GetQueuedCompletionStatus() 阻塞,等待下次完成通知。

最后 调度到了 线程A,_ftprintf() 走完,然后又打印一遍 A 时代立即完成时,接收到的数据,和 B 的一样。

这样,只发送了一份数据,在服务端却处理了两次。

我的情况是 A 中接收的数据,是已经完全接收完了,不是说:只接收到一部分(通过立即返回表现),没接收到的数据,变成 交叠 IO 投递到 IOCP 上了(要是这种情况倒是很容易想通)。

我的数据两次(一次,WSARecv()立即返回得到的;另一次,IOCP的交叠IO完成通知得到的)完全一样。

原本以为立即返回的IO,是不会投递未决的交叠IO的,没想到……

大家在开发高并发服务时,有遇到这种情况吗,是怎么管理这种 “立即返回 + Overlapped IO投递” 同时出现的问题呢?

PS.

A->B->A的调度顺序大概是因为那个 printf(),它与终端间的 IO 引发了 pending,就切到 B 了
...全文
259 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
lijianli9 2010-03-27
  • 打赏
  • 举报
回复
你集中到get函数中执行吧,那个是线程得到通知的线程,就是你发起的一个要求,得到处理了,
WinEggDrop 2010-03-27
  • 打赏
  • 举报
回复
你应该看Overlapped Socket I/O这一段,因为你的代码就是Overlapped Socket I/O

"If an overlapped operation completes immediately, WSARecv returns a value of zero and the lpNumberOfBytesRecvd parameter is updated with the number of bytes received and the flag bits indicated by the lpFlags parameter are also updated."

这里只是指出如果返回0的话,那么有多少bytes数据接收到的变量lpNumberOfBytesRecvd以及lpFlags变量也会更新,但没有指出数据是不是被复制进去Overlapped Buff中去,这有可能只是数据已经在系统缓冲区,但还没有被复制到应用层程序的Buff中去。看了不少官方或非官方的例子,没看到会处理这种情况,只是查看是否处于WSA_IO_PENDING状态。一般就if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()),那依我估计,数据并没有复制到应用层程序,当返回0时,只是通知应用程序有多少数据在缓冲区可以马上被读取到应用程序。
dengsf 2010-03-26
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 silon212 的回复:]
可是 MSDN WSARecv() 的参考中,我确实没找见说 “立即完成也会投递 IO 包” 的确切说法。

最相近的意思是:

If no error occurs and the receive operation has completed immediately, WSARecv returns zero. In this case, the completion routine will have already been scheduled to be called once the calling thread is in the alertable state.

请问 dengsf,你是指这个么?
[/Quote]

我也忘了具体在哪个位置,msdn的资料本来就零散,可能在其它相关API或索引目录中见过的。
不过我确定返回0也是放入队列的,因为第一次见到时也觉得意外,所以印象深刻。
samuelo 2010-03-26
  • 打赏
  • 举报
回复
楼主如要实验,可以给每个socket做两个计数器。一个记录recv和send的次数和,另一个记录GQCT返回的次数。看2个是不是一致。
samuelo 2010-03-26
  • 打赏
  • 举报
回复
如果2边都处理,既处理“立刻返回”又处理GQCT返回,有重复操作破坏内存的危险。
所以,干净有效的办法是不判断WSARecv和WSASend的返回值。
那2个函数有返回值,可能是出于函数完整性考虑吧。
个人想法。
silon212 2010-03-24
  • 打赏
  • 举报
回复
多谢各位。

我看了《Windows网络编程》的 IOCP 的 demo,和 MSDN lib 上 WSARecv() 的 demo,它们确实没有区分立即完成和 WSA_IO_PENDING 的区别,都直接用 WSAWaitForMultipleEvents() + WSAGetOverlappedResult() 或 GetQueuedCompletionStatus() 取投递完成的 IO 包。

WSARecv() ref: http://msdn.microsoft.com/en-us/library/ms741688%28VS.85%29.aspx

可是 MSDN WSARecv() 的参考中,我确实没找见说 “立即完成也会投递 IO 包” 的确切说法。

最相近的意思是:

If no error occurs and the receive operation has completed immediately, WSARecv returns zero. In this case, the completion routine will have already been scheduled to be called once the calling thread is in the alertable state.

请问 dengsf,你是指这个么?

另外,最搞笑的是在有人在 WSARecv() 的参考中跟帖说:

I have observed that when WSARecv returns zero, the callback, if given, is called before return from WSARecv function. Is this expected behavior?

看了他跟我的问题一样。
yinshisike 2010-03-24
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 samuelo 的回复:]
“立刻返回 + Overlapped IO投递”是正常的。
我想,所有投递都会在GetQueuedCompletionStatus里返回,不论事先有无立刻返回。
所以只要处理GQCT的返回就可以了。不用管是否曾立刻返回。
[/Quote]

当一端发送据在另一端投递接收前,就会发生这种情况.
但就是1楼所说的一样,同时会GetQueuedCompletionStatus里返回.
固在投递接收时不用处理,全部放到线程池中处理就可以了.
musezh2 2010-03-24
  • 打赏
  • 举报
回复
其实,iocp的recv和真正的接受数据分两步。
dengsf 2010-03-23
  • 打赏
  • 举报
回复
MSDN有讲的,成功或返回IOPENDING错误码的都是投递成功,结果会放入IOCP队列
samuelo 2010-03-23
  • 打赏
  • 举报
回复
“立刻返回 + Overlapped IO投递”是正常的。
我想,所有投递都会在GetQueuedCompletionStatus里返回,不论事先有无立刻返回。
所以只要处理GQCT的返回就可以了。不用管是否曾立刻返回。
silon212 2010-03-23
  • 打赏
  • 举报
回复
多谢,你是回复我的第一个人。我在MSDN中国社区也问了,还没人回答。这两天也在想你上面说到的原因,正在试验中。

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧