WEB开发网
开发学院软件开发VC 面向短连接的网络服务器 阅读

面向短连接的网络服务器

 2009-04-17 20:03:41 来源:WEB开发网   
核心提示:前几天同事用sf上的一个网络类库写了个服务器,一测试发现性能很差,面向短连接的网络服务器,最多每秒才能处理500次请求,并且是在网络很好的情况下,当连接结束后,PER_IO_DATA马上又通过投递AccepteEx准备下一个连接,隔两个交换机后客户端就只能收到200次/秒的正确响应了,同事忙着做其它事

前几天同事用sf上的一个网络类库写了个服务器。一测试发现性能很差。最多每秒才能处理500次请求,并且是在网络很好的情况下,隔两个交换机后客户端就只能收到200次/秒的正确响应了。同事忙着做其它事,改进服务器的任务就交给我了。

项目中客户端的请求仅是有20bytes的数据,并且只有一小部分需要服务器回复500bytes左右的数据。综合考虑各种网络模型后我决得IOCP模型更适合当前的应用。

IOCP模型的使用方法很多资料都有。《windows网络编程》(第二版)讲得很好。随书光盘中有使用IOCP模型的简单但很好的例子。我的服务器程序写完时还没看到这个示例,正被其它示例代码里的锁弄的晕头转向。当看到这个示例后发现那些锁都是多余的。剩下的代码基本上差不多。

有点不同的是PER_IO_DATA的处理上。所有的例子在往完成端口投递请求时都先分配了一个PER_IO_DATA,请求处理后马上释放。考虑到我的应用中所有请求全部是短连接,并且数据量很小,我觉得分配和释放是浪费的。每个PER_IO_DATA对应一个socket,当socket关闭后这个PER_IO_DATA不必释放,而是用来准备接受下一个连接socket。

下面是我写的工作线程代码:

DWORD WINAPI ServerWorkerThread(LPVOID lpparam)
{
    CIocpServer * pServer = (CIocpServer*)lpparam;
    DWORD BytesTransferred;
    SOCKET socket;
    LPPER_IO_OPERATION_DATA PerIoData;
    BOOL close_socket = FALSE;
    int ret;
    while(pServer->bRun)
    {
        ret = GetQueuedCompletionStatus(pServer->CompletionPort, &BytesTransferred,
            (LPDWORD)&socket, (LPOVERLAPPED *) &PerIoData, INFINITE);
        if (ret == ERROR_SUCCESS)
        {
            DWORD last_error = GetLastError();
            if(last_error == ERROR_SUCCESS)
                return 0;                //完成端口被关闭,退出
            if(ERROR_NETNAME_DELETED == last_error  
                || ERROR_OPERATION_ABORTED == last_error)
                close_socket = TRUE;    //socket被关闭 或者 操作被取消
            else
                continue;
        }

        if (BytesTransferred == 0)
        {
            if(socket == 0 && PerIoData == 0)
                break;

            closesocket(PerIoData->socket);

            pServer->Accept(PerIoData);

            continue;
        }

        if(PerIoData->eType == IO_EVENT_ACCEPT)
        {
            setsockopt(PerIoData->socket,SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&(pServer->m_server), sizeof(pServer->m_server) ) ;

            if(CreateIoCompletionPort((HANDLE) PerIoData->socket, pServer->CompletionPort, (DWORD) PerIoData->socket,0) == NULL)
                printf("CreateIoCompletionPort error:%d ", GetLastError());

            ret = pServer->Recv(PerIoData,BytesTransferred);
        }
        else if(PerIoData->eType == IO_EVENT_WSARECV)
        {
            ret = pServer->Recv(PerIoData,BytesTransferred);
        }
        else if(PerIoData->eType == IO_EVENT_WSASEND)
        {
            ret = pServer->Send(PerIoData,BytesTransferred);
        }

        if(ret == FALSE)
        {
            closesocket(PerIoData->socket);
            pServer->Accept(PerIoData);

        }
    }

    return 0;
}

可以看到,每个AccepteEx接入的客户端连接对应着一个PER_IO_DATA,并伴随着该连接的整个生命周期。当连接结束后,PER_IO_DATA马上又通过投递AccepteEx准备下一个连接。这样可以避免平凡的分配和释放内存。

Tags:面向 连接 网络

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接