IOCP问题。。。有点赵杰

ahniyilin 2009-09-26 04:12:52
关于IOCP的一些机制不是很清楚!!!
理解的不是很彻底,大家来帮帮忙撒!!呵呵!

先把代码贴上来,再问大家问题
显示监听线程的代码

//服务器接收线程函数定义
DWORD WINAPI AcceptThread(LPVOID lpParam)
{
CServer *pServer = (CServer*) lpParam;
sockaddr_in addrClient;
INT addrLen = sizeof(addrClient);
SOCKET ClientSocket;
while(TRUE)
{
ClientSocket = accept(pServer->m_ServerSocket,(SOCKADDR*)&addrClient,&addrLen);
if (ClientSocket == INVALID_SOCKET)
continue;

//创建工作者线程
pServer->UserSessionThread(ClientSocket);

//开辟内存区
PPER_HANDLE_DATA pPerHandle =
(PPER_HANDLE_DATA)::GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));
pPerHandle->socket = ClientSocket;
memcpy(&pPerHandle->addr,&addrClient,addrLen);
//关联完全端口
CreateIoCompletionPort((PPER_HANDLE_DATA)pPerHandle->socket,
pServer->m_hCompletionPort,(DWORD)pPerHandle,0);

//投递第一个Recv请求
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));
pPerIO->OperatorType = IO_RECV;

WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
//接收操作字节数的指针
DWORD dwRecv;
//指向标志位的指针
DWORD dwFlags = 0;
::WSARecv(pPerHandle->socket,&buf,1,&dwRecv,&dwFlags,&pPerIO->ol,NULL);
}
closesocket(ClientSocket);
return 1;
}

再是IOCP工作线程的代码

DWORD WINAPI CompletionThread(LPVOID lpParam)
{
CServer *pServer = (CServer*) lpParam;
//传输字节数
DWORD dwTrans;
PPER_HANDLE_DATA pPerHandle;
PPER_IO_DATA pPerIO;
while(TRUE)
{
BOOL bRect = ::GetQueuedCompletionStatus(pServer->m_hCompletionPort,
&dwTrans, //一次I/O操作,接收实际传输的字节数
(LPDWORD)&pPerHandle, //单据柄数据
(LPOVERLAPPED*)&pPerIO,
WSA_INFINITE);
if(!bRect)
{//在此套接字有错误发生
::closesocket(pPerHandle->socket);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}
if ( 0 == dwTrans &&
(pPerIO->OperatorType == IO_RECV ||pPerIO->OperatorType == IO_SEND))
{//套接字被对方关闭
::closesocket(pPerHandle->socket);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}
switch(pPerIO->OperatorType)
{
case IO_RECV: //完成一个接收请求
{
//继续接收
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
pPerIO->OperatorType = IO_RECV;
DWORD dwFlags = 0;
//投递一个Recv请求
::WSARecv(pPerHandle->socket,&buf,1,&dwTrans,&dwFlags,&pPerIO->ol,NULL);
}
break;
case IO_SEND: //完成一个发送请求
{
//省略代码
//参数也省略了
//投递一个发送请求
pPerIO->OperatorType = IO_SEND;
dwError=WSASend(m_hClientSocket......);
}
;
break;
case IO_END: //完成一个结束请求
;
break;
default :
;
}
}
return 1;
}


有几个不明白的地方:
1.监听线程里的投递第一个Recv请求后,以后的Recv请求都会在IOCP工作线程里执行(当然是同一客户端而言的)?

2.投递第一个Recv请求,是不是一定能接收到数据呢,前后的Recv请求有和联系和区别呢?

3.在IOCP工作线程,投递Recv请求后,接着继续投递第二Recv请求;投递Send请求后,也是接着投递第二个Send请求。两个请求可以同时处理,而不会相互干扰?这个问题也是困扰我2天了。比方说,接收到客户端请求后,我会不停地投递Send请求,发送数据给客户端(这时候pPerIO->OperatorType == IO_SEND),客户端如果在接收数据的同时,发送一条命令过来,我就是要Recv了。这个和我的Send不会冲突吧,如果系统是多核的,Recv请求和Send请求是不是可能同时在2个线程里运行?是不是每个投递请求的pPerIO的内存去都是不一样的,我这样理解对吗?还是我想错了?!
...全文
105 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
ahniyilin 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 crst_zh 的回复:]
对于IOCP,你的理解是对的.
[/Quote]
折磨我四五天了!!
唉!!
突然觉得自己原来什么都不太懂!!
crst_zh 2009-09-27
  • 打赏
  • 举报
回复
对于IOCP,你的理解是对的.
ahniyilin 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 crst_zh 的回复:]
引用 12 楼 ahniyilin 的回复:
晕了!!我一直都认为是GetQueedCompletionStatus获取一个IO,然后根据下面的判断去执行IO操作。。。
那我以前的思路不都是有问题的啊!!!!!!!
思路是这样的对不对?
1。创建IOCP对象,创建IOCP工作线程
2.SOCKET和IOCP对象关联
3.在程序其他地方,投递WSARecv和WSASend
4.GetQueuedCompletionStatus去判断是否IO完成,然后继续投递

再多问一个关于TCP的问题。。。TCP是不是可以保证服务器端按照什么顺序发,客户端就按照什么顺序收。。


TCP是面向连接的,所谓连接,只是逻辑上的一个概念,并不存在真实的连接,类似虚电路的意思
底层还是走IP协议,也就是到了IP层,数据会被分片,可能也会沿着不同的路径路由。
但是到了接收端,分组会被重组,这时候接收到的数据就和发送端一致了,看起来就成了流的概念了。


[/Quote]
我的思路和理解没问题吧?!
crst_zh 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 ahniyilin 的回复:]
引用 11 楼 fangle6688 的回复:
1、你每次调用WSARecv、WSASend,都是往IOCP队列中投递了一个IO请求
这些IO请求在IOCP队列中是先进先出的

2、IOCP队列自己会调度与之关联的工作线程
每当IOCP队列中有一个IO完成了,会有一个工作线程的GetQueedCompletionStatus返回
它会返回给你3个方面的信息:CompletionKey(通常包含SOCKET句柄)、Overlapped结构和实际IO字节数
其中的Overlapped就是你投递WSARecv或WSASend时传入的那个
通常的做法是,每次投递IO请求时,都传入一个新的,互不相同的Overlapped结构
这样,当GetQueedCompletionStatus返回时,你就可以通过Overlapped来判断是哪一个IO完成了

晕了!!我一直都认为是GetQueedCompletionStatus获取一个IO,然后根据下面的判断去执行IO操作。。。
那我以前的思路不都是有问题的啊!!!!!!!
思路是这样的对不对?
1。创建IOCP对象,创建IOCP工作线程
2.SOCKET和IOCP对象关联
3.在程序其他地方,投递WSARecv和WSASend
4.GetQueuedCompletionStatus去判断是否IO完成,然后继续投递

再多问一个关于TCP的问题。。。TCP是不是可以保证服务器端按照什么顺序发,客户端就按照什么顺序收。。
[/Quote]

TCP是面向连接的,所谓连接,只是逻辑上的一个概念,并不存在真实的连接,类似虚电路的意思
底层还是走IP协议,也就是到了IP层,数据会被分片,可能也会沿着不同的路径路由。
但是到了接收端,分组会被重组,这时候接收到的数据就和发送端一致了,看起来就成了流的概念了。

ahniyilin 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 fangle6688 的回复:]
1、你每次调用WSARecv、WSASend,都是往IOCP队列中投递了一个IO请求
这些IO请求在IOCP队列中是先进先出的

2、IOCP队列自己会调度与之关联的工作线程
每当IOCP队列中有一个IO完成了,会有一个工作线程的GetQueedCompletionStatus返回
它会返回给你3个方面的信息:CompletionKey(通常包含SOCKET句柄)、Overlapped结构和实际IO字节数
其中的Overlapped就是你投递WSARecv或WSASend时传入的那个
通常的做法是,每次投递IO请求时,都传入一个新的,互不相同的Overlapped结构
这样,当GetQueedCompletionStatus返回时,你就可以通过Overlapped来判断是哪一个IO完成了
[/Quote]
晕了!!我一直都认为是GetQueedCompletionStatus获取一个IO,然后根据下面的判断去执行IO操作。。。
那我以前的思路不都是有问题的啊!!!!!!!
思路是这样的对不对?
1。创建IOCP对象,创建IOCP工作线程
2.SOCKET和IOCP对象关联
3.在程序其他地方,投递WSARecv和WSASend
4.GetQueuedCompletionStatus去判断是否IO完成,然后继续投递

再多问一个关于TCP的问题。。。TCP是不是可以保证服务器端按照什么顺序发,客户端就按照什么顺序收。。
fangle6688 2009-09-26
  • 打赏
  • 举报
回复
1、你每次调用WSARecv、WSASend,都是往IOCP队列中投递了一个IO请求
这些IO请求在IOCP队列中是先进先出的

2、IOCP队列自己会调度与之关联的工作线程
每当IOCP队列中有一个IO完成了,会有一个工作线程的GetQueedCompletionStatus返回
它会返回给你3个方面的信息:CompletionKey(通常包含SOCKET句柄)、Overlapped结构和实际IO字节数
其中的Overlapped就是你投递WSARecv或WSASend时传入的那个
通常的做法是,每次投递IO请求时,都传入一个新的,互不相同的Overlapped结构
这样,当GetQueedCompletionStatus返回时,你就可以通过Overlapped来判断是哪一个IO完成了
ahniyilin 2009-09-26
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 coding_hello 的回复:]
第3个问题:Recv和Send两个操作按照IOCP的设计原理是会并行处理的,所以应该分别为每个操作分配一块内存来单独处理。
[/Quote]
我可这么理解吗?
同一个socket的Recv和Send是并行处理的,相互之间不会干扰?

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

在结构体单IO数据的内存里存入数据,Recv和Send会自己做自己的,不会影响。
来了Recv就做Recv,来了Send就做Send?
dulvtianya 2009-09-26
  • 打赏
  • 举报
回复
学习啦
野男孩 2009-09-26
  • 打赏
  • 举报
回复
第3个问题:Recv和Send两个操作按照IOCP的设计原理是会并行处理的,所以应该分别为每个操作分配一块内存来单独处理。
Johnny_Lx 2009-09-26
  • 打赏
  • 举报
回复
第3个问题我也不怎么清楚了,感觉上如果多核应该能起到真正的多线程作用,再看看有没有高人回答吧
Johnny_Lx 2009-09-26
  • 打赏
  • 举报
回复
Windows核心编程(第五版)笔记 第十章 同步和异步设备I/O(Synchronous and Asynchronous Device I/O) 里也有讲,windows网编编程会带有例子
ahniyilin 2009-09-26
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 johnny_lx 的回复:]
对了,忘了提醒,楼主可以看下windows核心编程,里边有几种模型的详细介绍
[/Quote]
windows网编编程吧?!
ahniyilin 2009-09-26
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 johnny_lx 的回复:]
首先,如果服务器要接受相当大的客户端请求时再用完成端口模型,否则其他模型简单些;
其次,IOCP模型你也可以把它当作是多线程方式,只是在开始的时候一下子像vector那样开辟一批线程,然后线程不够时候再开一批(个人的便于理解的想法。。。不知可以接受否),因为你用真正的多线程处理时,每当客户端请求来就开一个线程,这样效率比较低,所以这样考虑下来,每个线程都会是线程独立的,不会互相影响
[/Quote]
谢谢你的回答!
我的理解是IOCP模型是个系统自动维护的线程池,应该比我们自己写的性能要高。
我问的三个问题,其实最想问的是第三个!
我心里这么理解,但是需要别人给我确认下!或者指出我的错误理解。
要不然,抱着一问去做事情,闹心!呵呵!
Johnny_Lx 2009-09-26
  • 打赏
  • 举报
回复
对了,忘了提醒,楼主可以看下windows核心编程,里边有几种模型的详细介绍
Johnny_Lx 2009-09-26
  • 打赏
  • 举报
回复
首先,如果服务器要接受相当大的客户端请求时再用完成端口模型,否则其他模型简单些;
其次,IOCP模型你也可以把它当作是多线程方式,只是在开始的时候一下子像vector那样开辟一批线程,然后线程不够时候再开一批(个人的便于理解的想法。。。不知可以接受否),因为你用真正的多线程处理时,每当客户端请求来就开一个线程,这样效率比较低,所以这样考虑下来,每个线程都会是线程独立的,不会互相影响
ahniyilin 2009-09-26
  • 打赏
  • 举报
回复

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

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

这个也贴上来

18,356

社区成员

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

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