IOCP非常疑惑的问题:GetQueuedCompletionStatus到底是如何知道是发送还是接收的?

woyongfen102 2010-06-04 07:12:29
看过了很多代码,仍然非常的疑惑,查了很多资料依然没有人讲这个(当然,大家都在讲的是怎么知道是读还是些)。
可能我的问题不是很明确。

IOCP在客户端连接之后用CreateIoCompletionPort将新的套接字与完成端口联系在一起,并在第3个参数中传递了一个32位的DWORD,通常大家都是传递一个自定义类或结构体的指针进去。问题就在这里:
在工作线程中GetQueuedCompletionStatus调用取得的是同样一个类型的指针,然后网上的很多代码都是直接判断了自己自定义结构体或类中的一个域,然后来判断是接收还是发送。
我一直弄不明白,WINDOWS对自定义的类型没有限制,这个域的值IO完成端口到底是怎么填进去的?
它是如何知道把这个值放在哪儿的?
...全文
1087 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 visualeleven 的回复:]
BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED* lpOverlapped,
__in DWORD dwMillis……
[/Quote]

恩。。。小弟不才,就是不知道这个标记是如何来用的。开始还以为是我们在获得通知后系统给初始化好的,所以很疑惑系统怎么知道我们的标记在哪儿。不过现在有点清晰了,只等最后的理解是否通过了。
多谢大家!
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 mori3000 的回复:]
http://topic.csdn.net/u/20100603/09/95c879da-875f-404c-9328-584f149ef39d.html
我写的完成端口的例子,完整工程文件,你可以参照参照
[/Quote]

非常感谢mori3000。
看了你的代码,也是从中看出了这样的结构:
while(true)
{
bBack = GetQueuedCompletionStatus(..., &lpOverlapped, INFINITE);
ioNode=(PPER_IO_CONTEXT)lpOverlapped;//取得io节点
switch(ioNode->IoOperation)//根据传进的操作类型进行操作
{
case IoAccept:
DoAccept(...);
ioNode->IoOperation=IoRead;
iResult = WSARecv(...);
break;
case IoRead:
if(dwNumberBytes>0)
{
ShowData(..., ioNode->wsaRBuffer.buf);
//接收数据完毕,继续投递一个读操作,等待接收数据
ioNode->IoOperation=IoRead;
iResult = WSARecv(...);
}
else//当接收数据为0,则说明对方断开了连接
{
DisConnect(...);
}
break;
case IoWrite:
...
}

看到,在第一次IoOperation==IoAccept时,处理一些连接信息,连接成功后马上投递了一个WSARecv(...)(此时ioNode->IoOperation=IoRead,就是为了下次GetQueuedCompletionStatus时我们知道,IO完成端口帮我们完成了一个Recv)这就是在等着下一次数据到来吧?然后在ioNode->IoOperation==IoRead时,先处理了本次接收到的数据,然后接着又投递了一个WSARecv(...),准备下一次数据的到来。
也就是说,Accept成功后,要马上(必须)投递一个WSARecv(...),在以后每次Recv成功后,必须再投递一个WSARecv(...)。至于WSASend的话可以根据需要来投递。

大家看看,我这样理解是不是完全正确了?
Eleven 2010-06-07
  • 打赏
  • 举报
回复
BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED* lpOverlapped,
__in DWORD dwMilliseconds
);
自定义的结构体中不是有指定的标记吗
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
多谢大家的回复。
准备结贴了。不过疑惑还是没有清晰。
楼上的很多回复的朋友都是知道答案的,只是我的问题可能表达的还是不够清楚。
看了很多代码上面都是那样写的:
WorkerThread(..)//伪代码
{
while(true)
{
GetQueuedCompletionStatus(..);
switch(pStructure->IoOperation)//根据传进的操作类型进行操作
{
case IoAccept:
doSomething();
break;
case IoRead:
doSomething();
break;
case IoWrite:
doSomething();
break;
}
}
}

这么说pStructure->IoOperation是我们自己在做某项操作的时候自己填进去的,然后把这个任务(recv,send,accept)丢给IO完成端口,然后GetQueuedCompletionStatus得到了操作状态结果。是这样的吧?
这么说来,IOCP的架构里,除了需要
while(true)
{
GetQueuedCompletionStatus(..);
。。。;
}
之外,还需要每个socket一直不停的recv()?
send的话可以根据需要发送的时机来调用,但recv是不是还是需要一直不停的调用?否则我们这么知道什么时候调用recv呢?很多人简单的说,只要判断我们自定义的结构里的那个OperationCode值就可以,但我的问题就是:这个值到底是谁来填的(现在好像确认了是自己来填),什么时候什么情况下填呢?
末日3000 2010-06-07
  • 打赏
  • 举报
回复
http://topic.csdn.net/u/20100603/09/95c879da-875f-404c-9328-584f149ef39d.html
我写的完成端口的例子,完整工程文件,你可以参照参照
scq2099yt 2010-06-07
  • 打赏
  • 举报
回复
主要是处理接受,如果要处理发送是为了验证发送是否成功后再做其他处理
woyongfen102 2010-06-07
  • 打赏
  • 举报
回复
原来登录错了账号。。
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
那个结贴按钮怎么点了没反应~
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
123123123123123123123
woyongfen101 2010-06-07
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 mori3000 的回复:]
对的,主要是处理接收,其实发送是无需处理的.处理发送就是再置标志成接收,这一点也是我琢磨了好久才明白,最后通过实例验证的
[/Quote]

其实发送是无需处理的.处理发送就是再置标志成接收
很不幸,又被这句弄糊涂了。。。。
是不是是说:在我们投递了WSASend之后,GetQueuedCompletionStatus得到了WSASend完成的消息,里面的IoOperation==IoSend,这时候说明我们的Send操作已经成功完成了。为了我们能继续接收到数据,我们需要把IoOperation重新设为IoRecv?
末日3000 2010-06-07
  • 打赏
  • 举报
回复
对的,主要是处理接收,其实发送是无需处理的.处理发送就是再置标志成接收,这一点也是我琢磨了好久才明白,最后通过实例验证的
昨夜无风 2010-06-05
  • 打赏
  • 举报
回复
道理很简单:

你WEASend(完成键里为send) -> GetQueuedCompletionStatus响应,获取完成键 -> 判断消息类型(此时为send),处理

你WEARecv(完成键里为recv) -> GetQueuedCompletionStatus响应,获取完成键 -> 判断消息类型(此时为recv),处理
ahniyilin 2010-06-05
  • 打赏
  • 举报
回复
接着上一层楼的回=====

单IO数据中的结构体中的 OperatorType 就表示操作类型

这个操作类型在你投递send或者recv等操作的时候,你可以设置的

在完成一个操作时,GetQueuedCompletionStatus获取单IO数据体中的缓冲区时候,你可以获取到你投递时候的操作类型
ahniyilin 2010-06-05
  • 打赏
  • 举报
回复
第一:IOCP是你先把请求投递出去(send操作 和 recv操作),至于何时完成这个投递操作,你也不知道,也不需要知道,你投递的一系列操作会形成一个投递操作队列,那何时得到你投递操作完成的返回信息呢?下面第二点讲
第二:你创建的工作者线程(一般是cpu个数*2),里面的GetQueuedCompletionStatus函数可以返回你完成的投递操作内容,至于怎么判断是那个客户传过来的,GetQueuedCompletionStatus函数里的完成键可以告诉你(完成键一般都是一个单句柄数据结构体,而这个结构体里有一个socket参数),可以依据这个socket判断是哪个客户端(我现在的一个项目就这么做的)。
第三:在完成一个投递操作时,GetQueuedCompletionStatus中的单IO数据体中的缓冲区就是你接收到的数据,在你进行send投递的时候,你只需要创建一个投递就可以了(在此之前需要把对应的socket和IOCP关联起来)
第四:你写成一个结构体,如果这个结构体是成员有A和B,但是两个不是同时可以使用的,你是强行把两个绑在一起,这就比如2个函数一样,其实你可以强行写成一个函数,但是你不觉得分开可以使你更清晰吗?

我把我的两个结构体发给你看看:

//单句柄数据
typedef struct _PER_HANDLE_DATA
{
SOCKET socket; //对应的套接字
sockaddr_in addr; //客户端地址
}PER_HANDLE_DATA,*PPER_HANDLE_DATA;

//单IO数据
typedef struct _PER_IO_DATA
{
OVERLAPPED ol; //重叠结构 必须在第一个字段
CHAR buf[BUFFER_ALL_SIZE]; //数据缓冲区
INT OperatorType; //操作类型
}PER_IO_DATA,*PPER_IO_DATA;

dinona 2010-06-05
  • 打赏
  • 举报
回复
GetCompletionResult修改为-->GetQueuedCompletionStatus
dinona 2010-06-05
  • 打赏
  • 举报
回复
但如果是接收呢?
CreateIoCompletionPort线程获得通知唤醒工作线程,这个时候应该是接收数据,应该要调用wsarecv。但在调用wsarecv我怎么才能知道是一个接收数据的操作呢?毕竟数据是由Client发送来的。

你要在客户发数据过来之前投递WSARecv操作,这个操作会一直等待到有数据到来,GetCompletionResult将会返回,这样你就知道是客户发过来的信息.
demonstyle 2010-06-05
  • 打赏
  • 举报
回复
iocp模型需要定义两个数据结构:
一个包含套接字信息,在与完成端口关联时作为完成键传递进去的,也就是你说的CreateIoCompletionPort中的第三个参数。
另一个数据结构则是包含相关io操作信息。你在投递WSASend和WSARecv时,将这个数据结构传递进去。在
GetQueuedCompletionStatus返回时,通过该数据结构的信息判断是发送还是接收。
woyongfen102 2010-06-04
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 lijianli9 的回复:]
你提交的wsasend或者wsarecv会在里面有一个完成键,里面可以包含你的操作类型,可以用这个来标识。
[/Quote]

你的意思是不是说这个值是自己指定的,而并非是完成端口来赋值的?
比如在我需要发送数据的时候用wsasend,里面的参数里我指定一个OP_SEND,这样在完成端口获得通知的时候里面就表明了我刚刚的wsasend是一个OP_SEND的操作结果。

但如果是接收呢?
CreateIoCompletionPort线程获得通知唤醒工作线程,这个时候应该是接收数据,应该要调用wsarecv。但在调用wsarecv我怎么才能知道是一个接收数据的操作呢?毕竟数据是由Client发送来的。
lijianli9 2010-06-04
  • 打赏
  • 举报
回复
你提交的wsasend或者wsarecv会在里面有一个完成键,里面可以包含你的操作类型,可以用这个来标识。

18,356

社区成员

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

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