前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于重叠IO模型的 回显TCP服务器设计

基于重叠IO模型的 回显TCP服务器设计

作者头像
用户1154259
发布2018-01-17 14:51:48
9890
发布2018-01-17 14:51:48
举报

---------------------1 套接字对象----------------------

为每个套接字创建一个SOCKET_OBJ对象,记录与之相关的信息。

 typedef struct _SOCKET_OBJ{
    SOCKET s;
    int nOutstandingOps;//重叠IO数量
    LPFN_ACCEPTEX lpfnAcceptEx; //扩展AcceptEx指针
}SOCKET_OBJ,*PSOCKET_OBJ;

所有重叠IO提交到特定的套接字上,释放对应套接字对象,必须保证此套接字再没有重叠IO,即nOutstandingOps=0

申请套接字对象,释放套接字对象的函数

 PSOCKET_OBJ GetSocketObj(SOCKET s){
     PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR,sizeof(SOCKET_OBJ));
     if(pSocket != NULL)
         pSocket->s = s;
     return pSocket;
 }
 void FreeSocketObj(PSOCKET_OBJ pSocket){
     if(pSocket->s != INVALID_SOCKET)
         ::closesocket(pSocket->s);
     ::GlobalFree(pSocket);
 }

---------------------2 缓冲区对象---------------------

缓冲区对象SOCKET_OBJ,记录重叠IO的所有属性

 typedef struct _BUFFER_OBJ{
    OVERLAPPED ol;//重叠结构
    char *buff;//使用的缓冲区
    int nLen;//buff长度
    PSOCKET_OBJ pSocket;//次io所属的套接字对象
    int nOperation;//提交的操作类型
#define OP_ACCEPT    1
#define OP_READ        2
#define OP_WRITE    3
    SOCKET sAccept;//保存AcceptEx接受客户套接字
    _BUFFER_OBJ *pNext;
 }BUFFER_OBJ,*PBUFFER_OBJ;

pNext将BUFFER_OBJ对象练成一个链表

事件句柄数组 和 链表地址

HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS];
int g_nBufferCount;//数量
PBUFFER_OBJ g_pBufferHead,g_pBufferTail;//地址

调用重叠IO函数之前,都要申请BUFFER_OBJ对象,记录信息。IO完成后,再释放BUFFER_OBJ对象

申请BUFFER_OBJ对象 的函数:

PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket,ULONG nLen){
    if(g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS)
        return NULL;
    PBUFFER_OBJ pBuffer = (PBUFFER_OBJ)::GlobalAlloc(GPTR,sizeof(BUFFER_OBJ));
    if(pBuffer!=NULL)
    {
        pBuffer->buff = (char*)::GlobalAlloc(GPTR,nLen);
        pBuffer->ol.hEvent = ::WSACreateEvent();
        pBuffer->pSocket = pSocket;
        pBuffer->sAccept = INVALID_SOCKET;

        //将新的BUFFER_OBJ添加到列表
        if(g_pBufferHead == NULL)
        {
            g_pBufferHead = g_pBufferTail = pBuffer;
        }
        else
        {
            g_pBufferTail->pNext = pBuffer;
            g_pBufferTail = pBuffer;
        }
        g_events[++g_nBufferCount] = pBuffer->ol.hEvent;
    }
    return pBuffer;
}

释放BUFFER_OBJ对象函数:

void FreeBufferObj(PBUFFER_OBJ pBuffer){
    //从列表中移除BUFFER_OBJ对象
    PBUFFER_OBJ pTest = g_pBufferHead;
    BOOL bFind = FALSE;
    if(pTest == pBuffer){
        g_pBufferHead = g_pBufferTail = NULL;
        bFind = TRUE;
    }
    else{
        while(pTest!=NULL && pTest->pNext!=pBuffer)
            pTest = pTest->pNext;
        if(pTest!=NULL){
            pTest->pNext = pBuffer->pNext;
            if(pTest->pNext == NULL)
                g_pBuffer Tail = pTest;
            bFind = TRUE;
        }
    }
    //释放它占用的空间
    if(bFind)
    {
        g_pBufferCount--;
        ::CloseHandle(pBuffer->ol.hEvent);
        ::GlobalFree(pBuffer->buff);
        ::GlobalFree(pBuffer);
    }
}

提交重叠IO,传递参数有重叠结构IO和缓冲区指针buff。在IO完成后,得到的是受信事件对象的句柄。根据这个句柄找到对应的BUFFER_OBJ对象。

查找BUFFER_OBJ对象的代码:

PBUFFER_OBJ FindBufferObj(HANDLE hEvent){
    PBUFFER_OBJ pBuffer = g_pBufferHead;
    while(pBuffer != NULL){
        if(pBuffer->ol.hEvent == hEvent)
            break;
        pBuffer = pBuffer->pNext;
    }
    return pBuffer;
}

更新时间句柄数组g_events中的内容:

void RebuildArray(){
    PBUFFER_OBJ pBuffer = g_pBufferHead;
    int i= 1;
    while(pBuffer != NULL){
        g_events[i++] = pBuffer->ol.hEvent;
        pBuffer = pBuffer->pNext;
    }
}

---------------------3 提交重叠IO---------------------

投递IO之后,线程在重叠IO事件上等待,一旦IO事件对象受信,等待函数就会返回

提交接受连接的BUFFER_OBJ对象代码:

BOOL PostAccept(PBUFFER_OBJ pBuffer){
    PSOCKET_OBJ pSocket = pBuffer->pSocket;
    if(pSocket->lpfnAcceptEx != NULL){
        //设置IO类型,增加套接字上的重叠IO计数
        pBuffer->nOperation = OP_ACCEPT;
        pSocket->nOutstandingOps++;
        //投递此重叠IO
        DWORD dwBytes;
        pBuffer->sAccept = ::WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
        BOOL b = pSocket->lpfnAcceptEx(
            pSocket->s,
            pBuffer->sAccept,
            pBuffer->buff,
            BUFFER_SIZE - ((sizeof(sockaddr_in+16))*2),
            sizeof(sockaddr_in)+16,
            sizeof(sockaddr_in)+16,
            &dwBytes,
            &pBuffer->ol
            );
        if(!b){
            if(::WSAGetLastError()!=WSA_IO_PENDING)
                return FALSE;
        }
        return TRUE;
    }
    return FALSE;
};

接收数据的BUFFER_OBJ对象代码:

BOOL PostRecv(PBUFFER_OBJ pBuffer){
    //设置IO类型,增加套接字上的重叠IO计数
    pBuffer->nOperation = OP_ACCEPT;
    pBuffer->pSocket->nOutstandingOps++;
    //投递此重叠IO
    DWORD dwBytes;
    DWORD dwFlags = 0;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(::WSARecv(pBuffer->pSocket->s,&buf,1,&dwBytes,&dwFlags,&pBuffer->ol,NULL)!=NO_ERROR){
        if(::WSAGetLastError()!= WSA_IO_PENDING)
            return FALSE;
    }
    return TRUE;
}

发送数据额BUFFER-obj对象代码:

BOOL PostSend(PBUFFER_OBJ pBuffer){
    //设置IO类型,增加套接字上的重叠IO计数
    pBuffer->nOperation = OP_ACCEPT;
    pBuffer->pSocket->nOutstandingOps++;
    //投递此重叠IO
    DWORD dwBytes;
    DWORD dwFlags;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(::WSASend(pBuffer->pSocket->s,&buf,1,&dwBytes,&dwFlags,&pBuffer->ol,NULL)!=NO_ERROR){
        if(::WSAGetLastError()!=WSA_IO_PENDING)
            return FALSE;
    }
    return TRUE;
}

---------------------4 主函数---------------------

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012-10-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档