关于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的内存去都是不一样的,我这样理解对吗?还是我想错了?!

解决方案 »

  1.   


    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;
    这个也贴上来
      

  2.   

    首先,如果服务器要接受相当大的客户端请求时再用完成端口模型,否则其他模型简单些;
    其次,IOCP模型你也可以把它当作是多线程方式,只是在开始的时候一下子像vector那样开辟一批线程,然后线程不够时候再开一批(个人的便于理解的想法不知可以接受否),因为你用真正的多线程处理时,每当客户端请求来就开一个线程,这样效率比较低,所以这样考虑下来,每个线程都会是线程独立的,不会互相影响
      

  3.   

    对了,忘了提醒,楼主可以看下windows核心编程,里边有几种模型的详细介绍
      

  4.   

    谢谢你的回答!
    我的理解是IOCP模型是个系统自动维护的线程池,应该比我们自己写的性能要高。
    我问的三个问题,其实最想问的是第三个!
    我心里这么理解,但是需要别人给我确认下!或者指出我的错误理解。
    要不然,抱着一问去做事情,闹心!呵呵!
      

  5.   

    Windows核心编程(第五版)笔记 第十章 同步和异步设备I/O(Synchronous and Asynchronous Device I/O) 里也有讲,windows网编编程会带有例子
      

  6.   

    第3个问题:Recv和Send两个操作按照IOCP的设计原理是会并行处理的,所以应该分别为每个操作分配一块内存来单独处理。
      

  7.   

    我可这么理解吗?
    同一个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?
      

  8.   

    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完成了
      

  9.   

    晕了!!我一直都认为是GetQueedCompletionStatus获取一个IO,然后根据下面的判断去执行IO操作
    那我以前的思路不都是有问题的啊!!!!!!!
    思路是这样的对不对?
    1。创建IOCP对象,创建IOCP工作线程
    2.SOCKET和IOCP对象关联
    3.在程序其他地方,投递WSARecv和WSASend
    4.GetQueuedCompletionStatus去判断是否IO完成,然后继续投递再多问一个关于TCP的问题TCP是不是可以保证服务器端按照什么顺序发,客户端就按照什么顺序收。。
      

  10.   


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