还有些问题继续请教unsigned大哥!
我的工作线程中处理代码如下,但根据我的理解,红色标注的问题1处释放的应该是GetQueuedCompletionStatus()函数对应的那个有事件完成通知的socket的PerHandleData和PerIOData吧,这样的话下面就没法执行了,肯定是错误的,但是该如何才能得到对应的每一个已经accept到的socket对应的PerHandleData和PerIOData,以便我在close时将其对应的PerHandleData和PerIOData也释放呢? 
请赐教!多谢!
while(1)
    {
        time_t NowTime = time(0);        GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIOData, INFINITE);        CurSock = PerHandleData->Socket;
        pthis->m_NELinkPool.GetSocketInfo(CurSock, &CurSockInfo);        if ((NowTime > CheckLinkTime) && (NowTime - CheckLinkTime) > 30)
        {            
            // 检查所有socket是否超时。如果超时,关闭socket. 并同步NE连接池和信息表
            for (bool b=pthis->m_NELinkPool.MoveBegin(&CurSockInfo); b; b=pthis->m_NELinkPool.MoveNext(&CurSockInfo))
            {
                if (difftime(time(0), CurSockInfo.LastTime) > 1200)
                {
                    SOCKET tempsock = CurSockInfo.Socket;
                    closesocket(tempsock);
                    pthis->m_NELinkPool.DeleteSocket(tempsock);
                    pthis->m_BeapTable.DeleteSocket(tempsock);
                    pthis->DoNESocketTimeoutEvent(tempsock);                    GlobalFree(PerHandleData); // 问题1
                    GlobalFree(PerIOData); // 问题2
                }                int result = send(CurSockInfo.Socket, "keep_alive_msg", strlen("keep_alive_msg"), 0);
            }            CheckLinkTime = NowTime;
        }        // 首先检查套接字上是否发生错误,如果发生了则关闭套接字并清楚同套接字相关的SOCKET_INFORATION结构体
        if (BytesTransferred == 0 && (PerIOData->OperationType == RECV_POSTED || PerIOData->OperationType == SEND_POSTED))
        {            
            printf("Closing Socket %d, code = %d\n", PerHandleData->Socket, WSAGetLastError());
            closesocket(PerHandleData->Socket);
            pthis->m_NELinkPool.DeleteSocket(PerHandleData->Socket);
            pthis->m_BeapTable.DeleteSocket(PerHandleData->Socket);            GlobalFree(PerHandleData);
            GlobalFree(PerIOData);            continue;
        }        // 接收到数据时
        if (PerIOData->OperationType == RECV_POSTED)
        {
            actlen = BytesTransferred;
            memset(buf, 0, sizeof(buf));
            strncpy(buf, PerIOData->Buffer, actlen);
            pthis->m_NELinkPool.SetCurTime(CurSock, time(NULL));
            pthis->DoNERecvDataEvent(CurSock, CurSockInfo.ip, buf, actlen);            // 获取完整包
            proxy = NO_PROXY;
            string pack = "";
            BEAP_FACTORY factoryVer = UNKNOWN;
            
            for (bool b=pthis->m_NELinkPool.GetFirstPacket(CurSock, buf, actlen, pack, &proxy, &factoryVer);
                b;
                b=pthis->m_NELinkPool.GetNextPacket(CurSock, pack, &proxy, &factoryVer))
            {
                    // 注意:curSockInfo中laststr少了刚刚接收的信息, 如果以下要使用laststr字段,需修改代码
                    pthis->DealNEEvent(pack.c_str(), static_cast<int>(pack.size()), proxy, CurSockInfo, factoryVer);
            }
        }        Flags = 0;
        ZeroMemory(&(PerIOData->OVerlapped), sizeof(OVERLAPPED));        PerIOData->DATABuf.buf = PerIOData->Buffer;
        PerIOData->DATABuf.len = DATA_BUFSIZE;        if (WSARecv(PerHandleData->Socket, &(PerIOData->DATABuf), 1, &RecvBytes, &Flags, &(PerIOData->OVerlapped), NULL) == SOCKET_ERROR)
        {
                if (WSAGetLastError() != ERROR_IO_PENDING)
                {
                    printf("WSARecv() failed with error %d\n", WSAGetLastError());                    closesocket(PerHandleData->Socket);
                    pthis->m_NELinkPool.DeleteSocket(PerHandleData->Socket);
                    pthis->m_BeapTable.DeleteSocket(PerHandleData->Socket);
                    GlobalFree(PerHandleData);
                    GlobalFree(PerIOData);
                }
        }
    }

解决方案 »

  1.   

            if ((NowTime > CheckLinkTime) && (NowTime - CheckLinkTime) > 30)
            {            
                // 检查所有socket是否超时。如果超时,关闭socket. 并同步NE连接池和信息表
                for (bool b=pthis->m_NELinkPool.MoveBegin(&CurSockInfo); b; b=pthis->m_NELinkPool.MoveNext(&CurSockInfo))
                {
                    if (difftime(time(0), CurSockInfo.LastTime) > 1200)
                    {
                        SOCKET tempsock = CurSockInfo.Socket;
                        closesocket(tempsock);
                        pthis->m_NELinkPool.DeleteSocket(tempsock);
                        pthis->m_BeapTable.DeleteSocket(tempsock);
                        pthis->DoNESocketTimeoutEvent(tempsock);                    GlobalFree(PerHandleData); // 问题1
                        GlobalFree(PerIOData); // 问题2
                    }                int result = send(CurSockInfo.Socket, "keep_alive_msg", strlen("keep_alive_msg"), 0);
                }            CheckLinkTime = NowTime;
            }这段检查代码请放到定时器当中,所有的删除操作都不要做,只需要调用closesocket。如果需要发送数据,请在CloseSocket之前发送。
      

  2.   

    你可以下载我之前写的这份代码做参考http://download.csdn.net/source/727569
    对于超时检查的部分流程大致是:
    定时器(Unit_PurifierTimer.cpp,多媒体定时器,所以只有一个定时器事件处理函数PurifierTimerProc)
    到达一定时间,触发回收事件SetEvent回收线程函数体(Unit_PurifierThread.cpp)在此之前等待两个事件,一个是程序退出事件,另一个则是上面的提到的回收事件
    1.退出事件触发,结束线程
    2.回收事件触发,进行回收操作,判断有符合条件的则调用DumpConnection(Unit_Connections.cpp),置入回收站,并设置回收时间。接着对回收站当中的连接进行清理,收回之后过一段时间,则清除。象这种逻辑实际上是为了方便使得不会在多个线程当中对同一个内存块进行重复释放。任何时间里面,只要判断到需要收回连接,则置入回收站(除非本来就在回收站),过一段时间自动由定时线程进行回收。
      

  3.   

    其实我不需要做的那么麻烦,我程序的流程就是:while循环用来接收每个socket上收到的数据,同时每隔30秒对所有accept到的socket进行一次轮训检查,发现1200s内一直没有在某个socket上接收到数据(每次收到数据都会将当前时间保存,以便判断)【认为连接超时】,则将这个socke关闭。先前我是用的select方式,只要closesocket就OK了!
    但是现在用了完成端口实现,完成端口每次accept一个socket后都会为其GlobalAlloc两个对应的空间,PerIOData和PerHandleData,问题就是我在上述判断连接超时时如果仅调用closesocket的话就没有将socket对应的PerIOData和PerHandleData释放,肯定要造成内存泄露的吧。可是如果要释放的话我又该怎么才能将需要关闭的socket对应的PerIOData和PerHandleData释放掉呢?unsigned大哥的定时器回收做的很好,不过我想通过一种简单的办法实现,不知道有没有可行的方法呢?需要我每次accept一个socket后分配PerIOData和PerHandleData时将分配的地址保存起来吗?
    有点晕了
      

  4.   

    需要我每次accept一个socket后分配PerIOData和PerHandleData时将分配的地址保存起来吗? 
    有点晕了
    ==========
    保存socket就行了,定时器只须closesocket
      

  5.   

    int iSeconds;
    int ilenght = sizeof(iSeconds);
    for (list<IOBuffer*>::iterator iter = pThis->IOAceeptList.begin(); 
    iter != pThis->IOAceeptList.end(); iter ++)
    {
    if ((*iter)!= NULL)
    {
    ::getsockopt((*iter)->m_Socket, SOL_SOCKET, SO_CONNECT_TIME, (char *)&iSeconds, &ilenght);
    if (iSeconds != -1 && iSeconds > 120)
    {
    //非正常连接,关闭连接,并将SOCKET从ACCEPT队列中删除
    if ( (*iter)->m_Socket != INVALID_SOCKET )
    {
       closesocket((*iter)->m_Socket);
     (*iter)->m_Socket = INVALID_SOCKET;
    }你搞一个接收链表,在接收线程中不停止的getsockopt就可以,接收超时你可以直接关闭SOCKET, 你投递的ACCEPT会在GetQueuedCompletionStatus中响应,在这里可以回收你投递的ACCEPT的内存.
      

  6.   


    那为每个socket分配的PerIOData和PerHandleData系统会自动释放吗?还是通过别的方法来处理呢?
      

  7.   

    accept之后,对该socket做的第一件事是什么?WSARecv吧?既然有WSARecv,你closesocket是不是会导致该WSARecv请求失败,而产生一次完成通知?WokerThread是不是可以通过GetQueuedCompletionStatus取得PerHandleData和PerIOData,那又何尝会没有时间去释放?
      

  8.   

    WSAAccept接收的是从客户端发过来的第一块数据,这个并是WSARecv啊!!~~在WSAAccept时closeSocket的时候记的马上回收这块内存.
      

  9.   

    WSAAccept怎么会收到数据的?那是AcceptEx,AcceptEx也会有完成通知,跟WSARecv/WSASend是一样处理的.
      

  10.   

    把检查超时和超时的处理分到另外一个线程1,你放在这里,影响性能
        ,,,GetQueuedCompletionStatus和GetQueuedCompletionStatus之间,要尽量缩短时间
        ,,,一般这个地方,只处理判断操作(连接,接收,发送,错误),处理部分,一般都是剥离到另外的线程中做2,你放在这里,超时判断并不准确,如果没有IO操作完成,你不会执行到超时判断这一步代码
        ,,,如果在1200s内没有IO操作完成,你的程序就表现出bug了3,从socket 可以反取到该socket所在的PerHandleData和PerIOData