关于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的内存去都是不一样的,我这样理解对吗?还是我想错了?!
理解的不是很彻底,大家来帮帮忙撒!!呵呵!先把代码贴上来,再问大家问题
显示监听线程的代码 //服务器接收线程函数定义
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的内存去都是不一样的,我这样理解对吗?还是我想错了?!
解决方案 »
- VC2008+win7 怎样在XP风格主题下使复选框CHECKBOX透明?
- 有关MDAC版本的问题
- glLookAt()问题,高手们还等什么???
- VC6下怎样用GDI+实现对图像进行修改后保存?
- 项目中的链接错误问题(VC开发)
- 怎么实现对话框内的滚动条控件的滚动?(急,在线等)
- 请问要用vc作代理服务器要用到什么知识,技术,方法。。。。
- 好奇怪的问题小弟才疏学浅请指教
- void Navigate( LPCTSTR URL, DWORD dwFlags = 0, LPCTSTR lpszTargetFrameName = NULL, ...)中的lpszTargetFrameName是指什么?
- 不知道该不该问,一狠心,问吧
- 一个wave声音,一系列图片,如何合成一个avi文件?
- 关于PreTranslateMessage的一个一怪问题
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;
这个也贴上来
其次,IOCP模型你也可以把它当作是多线程方式,只是在开始的时候一下子像vector那样开辟一批线程,然后线程不够时候再开一批(个人的便于理解的想法不知可以接受否),因为你用真正的多线程处理时,每当客户端请求来就开一个线程,这样效率比较低,所以这样考虑下来,每个线程都会是线程独立的,不会互相影响
我的理解是IOCP模型是个系统自动维护的线程池,应该比我们自己写的性能要高。
我问的三个问题,其实最想问的是第三个!
我心里这么理解,但是需要别人给我确认下!或者指出我的错误理解。
要不然,抱着一问去做事情,闹心!呵呵!
同一个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?
这些IO请求在IOCP队列中是先进先出的2、IOCP队列自己会调度与之关联的工作线程
每当IOCP队列中有一个IO完成了,会有一个工作线程的GetQueedCompletionStatus返回
它会返回给你3个方面的信息:CompletionKey(通常包含SOCKET句柄)、Overlapped结构和实际IO字节数
其中的Overlapped就是你投递WSARecv或WSASend时传入的那个
通常的做法是,每次投递IO请求时,都传入一个新的,互不相同的Overlapped结构
这样,当GetQueedCompletionStatus返回时,你就可以通过Overlapped来判断是哪一个IO完成了
那我以前的思路不都是有问题的啊!!!!!!!
思路是这样的对不对?
1。创建IOCP对象,创建IOCP工作线程
2.SOCKET和IOCP对象关联
3.在程序其他地方,投递WSARecv和WSASend
4.GetQueuedCompletionStatus去判断是否IO完成,然后继续投递再多问一个关于TCP的问题TCP是不是可以保证服务器端按照什么顺序发,客户端就按照什么顺序收。。
TCP是面向连接的,所谓连接,只是逻辑上的一个概念,并不存在真实的连接,类似虚电路的意思
底层还是走IP协议,也就是到了IP层,数据会被分片,可能也会沿着不同的路径路由。
但是到了接收端,分组会被重组,这时候接收到的数据就和发送端一致了,看起来就成了流的概念了。