IO处理线程

时间:2022-04-22
本文章向大家介绍IO处理线程,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

客户IO处理,是在工作线程,_WorkerThreadProc中完成的

函数,在完成端口上调用GetQueuedCompletionStatus函数等待IO完成,并调用自定义函数HandleIO来处理IO,具体代码如下:

DOWRD WINAPI CIOCPServer::_WorkerThreadProc(LPVOID lpParam)
{
    #ifdef _DEBUG
        ::OutputDebugString("Worker Thread startup...n");
    #endif //_DEBUG

    CIOCPServer *pThis = (CIOCPServer*)lpParam;
    CIOCPBuffer *pBuffer;
    DWORD dwKey;
    DWORD dwTrans;
    LPOVERLAPPED lpol;

    while(TRUE)
    {
        //在关联到此完成端口的所有套接字上等待IO完成
        BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion,&dwTrans,(LPDWORD)&dwKey,(LPOVERLAPPED)&lpol,WSA_INFINITE);

        if(dwTrans == -1)
        {
            #ifdef _DEBUG
                ::OutputDebugString("Worker Thread startup...n");
            #endif //_DEBUG
                
            ::ExitThread(0);
        }
        
        pBuffer=CONTAINING_RECORD(lpol,CIOCPBuffer,ol);
        int nError = NO_ERROR;

        if(!bOK)
        {
            SOCKET s;

            if(pBuffer->nOperation == OP_ACCEPT)
            {
                s=pThis->m_sListen;
            }
            else
            {
                if(dwKey == 0)
                    break;
                s=((CIOCPContext*)dwKey)->s;
            }

            DWORD dwFlags = 0;

            if(!::WSAGetOverlappedResult(s,&pBuffer->ol,&dwTrans,FALSE,&dwFlags))
            {
                nError = ::WSAGetLastError();
            }
        }

        pThis->HandleIO(dwKey,pBuffer,dwTrans,nError);

    }
    #ifdef _DEBUG
        ::OutputDebugString("Worker Thread out...n");
    #endif //_DEBUG

    return 0;
}

SendText成员函数用于在连接上发送数据,执行时先申请一个缓冲区对象,把用户将要发送的数据复制到里面,然后调用postSend成员函数投递这个缓冲区对象

BOOL CIOCPServer::SendText(CIOCPContext *pContext,char *pszText,int nLen)
{
    CIOCPBuffer *pBuffer = AllocateBuffer(nLen);
    if(pBuffer != NULL)
    {
        memcpy(pBuffer->buff,pszText,nLen);
        return PostSend(pContext,pBuffer);
    }
    return FALSE;
}

下面的HandleIO函数是关键,

处理完成的IO,投递新的IO请求,释放完成的缓冲区对象,关闭客户上下文对象

下面是主要的实现代码:

void CIOCPServer::HandleIO(DWORD dwKey,CIOCPBuffer *pBuffer,DOWRD dwTrans,int nError)
{
    CIOCPContext *pContext = (CIOCPContext*)dwKey;

    #ifdef _DEBUG
        ::OutputDebugString("HandleIO startup..n");
    #endif //_DEBUG

    //减少套接字未决IO计数
    if(pContext!=NULL)
    {
        ::EnterCriticalSection(&pContext->Lock);

        if(pBuffer->nOperation == OP_READ)
            pContext->nOutstandingRecv--;
        else if(pBuffer->nOperation == OP_WRITE)
            pContext->nOutstandingSend--;

        ::LeaveCriticalSection(&pContext->Lock);

        //检查套接字是否已经打开
        if(pContext->bClosing)
        {

            #ifdef _DEBUG
                ::OutputDebugString("HandleIO startup..n");
            #endif //_DEBUG
            
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
            }
            //释放已关闭套接字的未决IO
            ReleaseBuffer(pBuffer);
            return;
        }
    }
    else
    {
        RemovePendingAccept(pBuffer);
    }

    //检查套接字上发生的错误,然后直接关闭套接字

    if(nError!=NO_ERROR)
    {
        if(pBuffer->nOperation != OP_ACCEPT)
        {
            OnConnectionError(pContext,pBuffer,nError);
            CloseAConnection(pContext);
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
            }

            #ifdef _DEBUG
                ::OutputDebugString("HandleIO startup..n");
            #endif //_DEBUG
        }
        else//在监听套接字上发生错误
        {
            if(pBuffer->sClient != INVALID_SOCKET)
            {
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }

            #ifdef _DEBUG
                ::OutputDebugString("HandleIO startup..n");
            #endif //_DEBUG
        }
        ReleaseBuffer(pBuffer);
        return;
    }

    //开始处理
    if(pBuffer->nOperation == OP_ACCEPT)
    {
        if(dwTrans == 0)
        {
            #ifdef _DEBUG
                ::OutputDebugString("HandleIO startup..n");
            #endif //_DEBUG

            if(pBuffer->sClient != INVALID_SOCKET)
            {
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }
        }
        else
        {
            //为接收新连接的申请客户上下文对象
            CIOCPContext *pClient = AllocateContext(pBuffer->sClient);
            if(pClient != NULL)
            {
                if(AddAConnection(pCliebt))
                {
                    //取得用户地址
                    int nLocalLen,nRmoteLen;
                    m_lpfnGetAcceptExSockaddrs(
                            pBuffer->buff,
                            pBuffer->nLen-((sizeof(sockaddr_in)+16)*2),
                            sizeof(sockaddr_in)+16,
                            sizeof(sockaddr_in)+16,
                            (SOCKADDR **)&pLocalAddr,
                            &nLocalLen,
                            (SOCKADDR **)&pRemoteAddr,
                            &nRmoteLen);
                    memcpy(&pClient->addrLocal,pLocalAddr,nLocalLen);
                    memcpy(&pClient->addrRemote,pRemoteAddr,nRmoteLen);

                    //关联新连接到完成端口对象
                    ::CreateIoCompletionPort((HANDLE)pClient->s,m_hCompletion,(DWORD)pClient,0);

                    //通知用户
                    pBuffer->nLen = dwTrans;
                    OnConnectionEstablished(pClient,pBuffer);

                    //向新连接投递Read请求
                    for(int i=0;i<5;i++)
                    {
                        CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
                        if(p != NULL)
                        {
                            if(!PostRecv(pClient,p))
                            {
                                CloseAConnection(pClient);
                                break;
                            }
                        }
                    }
                }
                else
                {
                    CloseAConnection(pClient);
                    ReleaseContext(pClient);
                }
            }
            else
            {
                //资源不足,关闭与客户的连接即可
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }
        }
        //Accept请求完成,释放IO缓冲区
        ReleaseBuffer(pBuffer);
        //通知监听线程继续再投递一个Accept请求
        ::InterlockedDecrement(&m_nRepostCount);
        ::SetEvent(m_hRepostEvent);
    }
    else if(pBuffer->nOperation == OP_READ)
    {
        if(dwTrans == 0)
        {
            //先通知用户
            pBuffer->nLen = 0;
            OnConnectionClosing(pContext,pBuffer);
            //再关闭连接
            CloseAConnection(pContext);
            //释放客户上下文和缓冲区对象
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
            }
            ReleaseBuffer(pBuffer);
        }
        else
        {
            pBuffer->nLen = dwTrans;
            //按照IO投递的顺序读取接收到的数据
            CIOCPBuffer *p = GetNextReadBuffer(pContext,pBuffer);
            while(p!=NULL)
            {
                OnReadCompleted(pContext,p);
                //增加要读的序列号的值
                ::InterlockedDecrement((LONG*)pContext->nCurrentReadSequence);
                //释放IO
                ReleaseBuffer(p);
                p = GetNextReadBuffer(pContext,NULL);
            }
            //继续投递一个新的请求
            pBuffer = AllocateBuffer(BUFFER_SIZE);
            if(pBuffer==NULL || !PostRecv(pContext,pBuffer))
            {
                CloseAConnection(pContext);
            }
        }
    }
    else if(pBuffer->nOperation == OP_WRITE)
    {
        if(dwTrans == 0)
        {
            //先通知用户
            pBuffer->nLen = 0;
            OnConnectionClosing(pContext,pBuffer);
            //再关闭连接
            CloseAConnection(pContext);
            //释放客户上下文和缓冲区对象
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
            }
            ReleaseBuffer(pBuffer);
        }
        else
        {
            //写操作完成,通知用户
            pBuffer->nLen = dwTrans;
            OnWriteCompleted(pContext,pBuffer);

            //释放SendText函数申请缓冲区
            ReleaseBuffer(pBuffer);
        }
    }
}