计算机网络 完成端口模型

首先 大致了解一下完成端口的结构和原理

 1.首先 完成端口的特性:

异步 非阻塞  效率较高

2.原理:

预先创建好n个空闲的Waiter套接字 

然后把这些套接字交给监听套接字管理 

再将监听套接字交给IOCP完成端口处理  

创建好我们的线程池

 如果有n客户端链接我们的服务器

 我们就新创建出n个空闲的线程  来维持空闲的线程数量平衡

然后向内核投递接受数据请求

将相应的Waiter交给IOCP管理

完整代码如下

类声明

#ifndef CTCPMAP_H
#define CTCPMAP_H

#include<list>
#include<winsock2.h>
#include<windows.h>
#include<iostream>
#include <Mswsock.h>
#include<map>
using namespace std;
#define MaxNum 64
#define NT_ACCEPT 0
#define NT_READ 1
struct DataInfo
{
    char* pszbuf;
    int  nPacksize;
    int  noffest;
};

struct MySocket
{
    OVERLAPPED olp;//用来发通知
    SOCKET socket;//套接字waiter
    char szbufp[1024];//接收缓冲区
    int  type;//判断网络类型
};

class CTCPmap
{
public:
    //构造
    CTCPmap();
    //析构
    ~CTCPmap();
    //初始化网络
    bool InitnewWork();
    //销毁网络
    void DeleteWork();
    //线程函数
    static DWORD WINAPI ThreadEventSelect(LPVOID lpvoid);
    static DWORD WINAPI ThreadIOCP(LPVOID lpvoid);
    static DWORD WINAPI ThreadSelect(LPVOID lpvoid);
    static DWORD WINAPI ThreadAccept(LPVOID lpvoid);
    static DWORD WINAPI ThreadRecv(LPVOID lpvoid);
    //发送数据
    bool sendDate(SOCKET sockWaiter,char*szbuf,int nLen);
    //接收数据
    void recvDate();
    bool PostAccept();
    bool PostRecv(MySocket*);


private:
    //监听者句柄
    SOCKET m_soclisten;
    //总线程池
    std::list<HANDLE> my_Threadpool;
    //服务者线程池
    std::list<SOCKET> my_ThreadWaiter;
    //我的套接字链表
    std::list<MySocket*> my_mysocketWaiter;
    //线程结束标识
    bool my_FlagQuit;
    //传服务者sockWaiter用到的容器
    std::map<DWORD,SOCKET> my_map;
    //结构体与套接字的映射
    std::map<SOCKET,DataInfo*> m_sdmap;
    fd_set m_fd;
    SOCKET m_arySocket[MaxNum];
    HANDLE m_aryEvent[MaxNum];
    int  m_nEventNum;
    HANDLE m_hIOCP;



};

#endif // CTCPMAP_H

类定义

#include "ctcpmap.h"

CTCPmap::CTCPmap()
{

    m_soclisten=0;
    my_FlagQuit=true;
    FD_ZERO(&m_fd);
    m_nEventNum=0;
    ZeroMemory(m_arySocket,sizeof(m_arySocket));
    ZeroMemory(m_aryEvent,sizeof(m_aryEvent));
    m_hIOCP=0;

}
CTCPmap::~CTCPmap()
{

}

bool CTCPmap::InitnewWork()
{

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;


    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {


        DeleteWork();
        return false;
    }



    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {

        WSACleanup();
        return false;
    }




//创建套接字
    m_soclisten=socket(AF_INET,SOCK_STREAM,0);
    if(m_soclisten==INVALID_SOCKET)
    {
       DeleteWork();
        return false;
    }
//绑定
    sockaddr_in addrserver;
    //绑定IP
    addrserver.sin_addr.S_un.S_addr=0;
    addrserver.sin_port=htons(8899);
    addrserver.sin_family=AF_INET;
    if(SOCKET_ERROR==bind(m_soclisten,(sockaddr*)&addrserver,sizeof(addrserver)))
    {
          DeleteWork();
            return false;

    }



//监听

    if(SOCKET_ERROR==listen(m_soclisten,10))

    {
        DeleteWork();
        return false;

    }
    //1.创建IOCP
    m_hIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,NULL);
    //2.将listen 交给IOCP
    CreateIoCompletionPort((HANDLE)m_soclisten,m_hIOCP,m_soclisten,0);
    //3.创建空闲Waiter
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    for(int i=0;i <si.dwNumberOfProcessors;i++)
    {
        if(!PostAccept())i--;
    }


    //4.创建线程池
    for(int i=0;i <si.dwNumberOfProcessors;i++)
    {
        HANDLE hand=CreateThread(0,0,&ThreadIOCP,this,0,0);
         if(hand)
            my_Threadpool.push_back(hand);
    }

//    HANDLE hevent=WSACreateEvent();

//    if(!WSAEventSelect(m_soclisten,hevent,FD_ACCEPT))
//    {
//        m_arySocket[m_nEventNum]=m_soclisten;
//        m_aryEvent[m_nEventNum]=hevent;
//        m_nEventNum++;
//    }

//    HANDLE hand=CreateThread(0,0,&ThreadEventSelect,this,0,0);
//    if(hand)
//       my_Threadpool.push_back(hand);

//    //将套接字加入到集合
//    FD_SET(m_soclisten,&m_fd);
//    //创建线程
//    HANDLE hand=CreateThread(0,0,&ThreadSelect,this,0,0);
//    if(hand)
//       my_Threadpool.push_back(hand);
//    HANDLE hand=CreateThread(0,0,&ThreadAccept,this,0,0);
//    if(hand)
//        my_Threadpool.push_back(hand);
//    hand=CreateThread(0,0,&ThreadRecv,this,0,0);
//    //创建服务者线程 加入到线程池
//    if(hand)
//        my_Threadpool.push_back(hand);

    return  true;


}

void CTCPmap::DeleteWork()
{
    //线程销毁
    my_FlagQuit=false;
    auto ite=my_Threadpool.begin();
    while(ite!=my_Threadpool.end())
    {
        if(WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
            TerminateThread(*ite,-1);
        CloseHandle(*ite);
        *ite=NULL;
        ite++;
    }

    my_Threadpool.clear();
     //关闭句柄
    if(m_soclisten)
        {
        closesocket(m_soclisten);
        m_soclisten=0;
    }

    //卸载服务器
    WSACleanup();
}

DWORD WINAPI CTCPmap::ThreadEventSelect(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;
    DWORD dwIndex;
    WSANETWORKEVENTS we;
    sockaddr_in addrclient;
    int  nsize=sizeof(addrclient);
    int  nRecvNum;
    while(pthis->my_FlagQuit)
    {
        //等事件
        dwIndex=WSAWaitForMultipleEvents(pthis->m_nEventNum,pthis->m_aryEvent,FALSE,WSA_INFINITE,0);
        dwIndex-=WSA_WAIT_EVENT_0;
        //判断发生什么事
        if(SOCKET_ERROR==WSAEnumNetworkEvents(pthis->m_arySocket[dwIndex],
                                               pthis->m_aryEvent[dwIndex],
                                               &we))
            continue;
        //处理
        if(we.lNetworkEvents&FD_ACCEPT)
        {
            cout<<"等待客户端连接"<<endl;
            SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);

            cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;

            //对应失败  跳出 继续循环
            if(sockWaiter==INVALID_SOCKET)continue;
            HANDLE hevent=WSACreateEvent();//人工的无信号事件

            if(!WSAEventSelect(sockWaiter,hevent,FD_READ|FD_CLOSE))
            {
                pthis->m_arySocket[pthis->m_nEventNum]=sockWaiter;
                pthis->m_aryEvent[pthis->m_nEventNum]=hevent;
                pthis->m_nEventNum++;
            }


        }

        if(we.lNetworkEvents&FD_READ)
            {
                DataInfo *pData=pthis->m_sdmap[pthis->m_arySocket[dwIndex]];
                //代表waiter 有数据来了
                if(!pData)
                {
                    pData=new DataInfo;
                    pData->noffest=0;
                    pData->nPacksize=0;
                    pthis->m_sdmap[pthis->m_arySocket[dwIndex]]=pData;
                    //waiter come
                    nRecvNum=recv(pthis->m_arySocket[dwIndex],(char*)&pData->nPacksize,sizeof(int),0);
                    if(pData->nPacksize>0)
                    {
                        pData->pszbuf=new char[pData->nPacksize];
                    }
                }
                else
                {
                    nRecvNum=recv(pthis->m_arySocket[dwIndex],pData->pszbuf+pData->noffest,pData->nPacksize,0);
                    if(nRecvNum>0)

                    {
                        pData->noffest+=nRecvNum;
                        pData->nPacksize-=nRecvNum;
                        if(pData->nPacksize==0)
                        {
                            //数据包接受完整
                            cout<<pData->pszbuf<<endl;
                            delete []pData->pszbuf;
                            pData->pszbuf=NULL;

                            delete [] pData;
                            pData=NULL;
                            pthis->m_sdmap[pthis->m_arySocket[dwIndex]]=NULL;

                        }
                    }
             }

             }

        if(we.lNetworkEvents&FD_CLOSE)
            {
                closesocket(pthis->m_arySocket[dwIndex]);
                WSACloseEvent(pthis->m_aryEvent[dwIndex]);
                if(pthis->m_nEventNum>1)
                {
                    pthis->m_arySocket[dwIndex]=pthis->m_arySocket[pthis->m_nEventNum-1];
                    pthis->m_aryEvent[dwIndex]=pthis->m_aryEvent[pthis->m_nEventNum-1];



                }
                pthis->m_nEventNum--;
            }



    }
    return  0;
}

DWORD CTCPmap::ThreadIOCP(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;
    DWORD dwNumberOfByTesTransferred;
    SOCKET sock;
    MySocket *pSocket;
    BOOL bflag;
    while(pthis->my_FlagQuit)
    {
        bflag=GetQueuedCompletionStatus(pthis->m_hIOCP,
                                        &dwNumberOfByTesTransferred,
                                        (PULONG_PTR)&sock,
                                        (LPOVERLAPPED*)&pSocket,
                                        INFINITE);
        if(!bflag)
        {
            continue;
        }
        if(!sock&&!pSocket)
            continue;
        switch(pSocket->type)
        {
            case NT_ACCEPT:
            pthis->PostAccept();
            pthis->PostRecv(pSocket);
            CreateIoCompletionPort((HANDLE)pSocket->socket,pthis->m_hIOCP,pSocket->socket,0);
            break;
            case NT_READ:
            cout<<pSocket->szbufp<<endl;
            pthis->PostRecv(pSocket);
            break;
        }

    }
}
//将集合交给select线程函数
DWORD WINAPI CTCPmap::ThreadSelect(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;
    fd_set fdtemp;
    TIMEVAL tv;
    tv.tv_sec=0;
    tv.tv_usec=100;
    sockaddr_in addrclient;

    int  nRecvNum;
    int  nsize=sizeof(addrclient);
    while(pthis->my_FlagQuit)
        {

        fdtemp =pthis->m_fd;
        //将套接字交给集合
         select(0,&fdtemp,0,0,&tv);
        if(FD_ISSET(pthis->m_soclisten,&fdtemp))
        {
            //代表有人连接我了
            SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);

            cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;

            //对应失败  跳出 继续循环
            if(sockWaiter==INVALID_SOCKET)continue;
            //将waiter加入到集合内
            FD_SET(sockWaiter,&pthis->m_fd);
        }

        //校验
        for(unsigned int i=1;i<pthis->m_fd.fd_count;i++)
        {


            if(FD_ISSET(pthis->m_fd.fd_array[i],&fdtemp))
            {
                DataInfo *pData=pthis->m_sdmap[pthis->m_fd.fd_array[i]];
                //代表waiter 有数据来了
                if(!pData)
                {
                    pData=new DataInfo;
                    pData->noffest=0;
                    pData->nPacksize=0;
                    pthis->m_sdmap[pthis->m_fd.fd_array[i]]=pData;
                    //waiter come
                    nRecvNum=recv(pthis->m_fd.fd_array[i],(char*)&pData->nPacksize,sizeof(int),0);
                    if(pData->nPacksize>0)
                    {
                        pData->pszbuf=new char[pData->nPacksize];
                    }
                }
                else
                {
                    nRecvNum=recv(pthis->m_fd.fd_array[i],pData->pszbuf+pData->noffest,pData->nPacksize,0);
                    if(nRecvNum>0)

                    {
                        pData->noffest+=nRecvNum;
                        pData->nPacksize-=nRecvNum;
                        if(pData->nPacksize==0)
                        {
                            //数据包接受完整
                            cout<<pData->pszbuf<<endl;
                            delete []pData->pszbuf;
                            pData->pszbuf=NULL;

                            delete [] pData;
                            pData=NULL;
                            pthis->m_sdmap[pthis->m_fd.fd_array[i]]=NULL;

                        }
                    }
                }


            }

        }

}

     return  0;

}

//接收客户端的线程函数
 DWORD WINAPI CTCPmap::ThreadAccept(LPVOID lpvoid)
 {
     CTCPmap *pthis=(CTCPmap*)lpvoid;

     sockaddr_in addrclient;
     int  nsize=sizeof(addrclient);

     u_long iMode=1;
     while(pthis->my_FlagQuit)
     {
         //店长接受客人 分配给服务员  --接受链接--accept();

         //服务者与他的客人对应起来
             SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);

             cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;

             //对应失败  跳出 继续循环
             if(sockWaiter==INVALID_SOCKET)continue;
             //对应成功 线程池添加了一个服务者线程

             //将Waiter 介入到链表 将waiter设置为非阻塞

             ioctlsocket(sockWaiter,FIONBIO,&iMode);

             //将服务者套接字加入到集合
             //FD_SET(sockWaiter,&(pthis->m_fd));

             pthis->my_ThreadWaiter.push_back(sockWaiter);






     }
     return  0;


 }

DWORD WINAPI CTCPmap::ThreadRecv(LPVOID lpvoid)
{
    CTCPmap *pthis=(CTCPmap*)lpvoid;

    while(pthis->my_FlagQuit)
    {
        // 接受
        pthis->recvDate();
        Sleep(10);

    }
    return  0;

}

void CTCPmap::recvDate()//接收函数
{

            // 服务员等客人说话 --recv();
                char *pszbuf=NULL;
                //当前位置标识
                int offset=0;
                //包长度
                int  nPackSize;

                int  nRecvNum;
                SOCKET sockWaiter;



                auto ite=my_ThreadWaiter.begin();
                while(ite!=my_ThreadWaiter.end())
                {
                    sockWaiter=*ite;

                        //jieshoubaodaxiao
                        nRecvNum=recv(sockWaiter,(char*)&nPackSize,sizeof(int ),0);
                        if(nRecvNum<=0)
                        {
                            //判断服务器是否下线
                            if(WSAGetLastError()==10054)
                            {
                                //删除映射
                                auto ite=my_map.begin();
                                while(ite!=my_map.end())
                                {
                                    if(ite->second==sockWaiter)
                                    {
                                        my_map.erase(ite);
                                        closesocket(sockWaiter);
                                        break;
                                    }
                                    //ite++;
                                }

                                break;
                            }
                            continue;
                            ite++;
                        }
                        //获得接受内容
                        pszbuf=new char[nPackSize];
                        while(nPackSize)//看当前剩余的还有多少没接受完
                        {
                            nRecvNum=recv(sockWaiter,pszbuf+offset,nPackSize,0);
                            if(nRecvNum>0)
                            {
                                nPackSize-=nRecvNum;
                                offset+=nRecvNum;
                            }
                        }
                        cout<<"client say:"<<pszbuf<<endl;
                        sendDate(sockWaiter,pszbuf,  nRecvNum);
                        delete []pszbuf;
                        pszbuf=NULL;





                     ite++;
                }




}

bool CTCPmap::PostAccept()
{

    MySocket* pSocket=new MySocket;
    pSocket->olp.hEvent=WSACreateEvent();
    pSocket->type=NT_ACCEPT;
    pSocket->socket=socket(AF_INET,SOCK_STREAM,0);
    if(!AcceptEx(m_soclisten,pSocket->socket,pSocket->szbufp,0,sizeof(sockaddr)+16,sizeof(sockaddr)+16,0,&pSocket->olp))
    {
        if(WSAGetLastError()!=ERROR_IO_PENDING)
        {
            WSACloseEvent(pSocket->olp.hEvent);
            closesocket(pSocket->socket);
            delete pSocket;
            pSocket=NULL;
            return  false;
        }
    }
    my_mysocketWaiter.push_back(pSocket);
    return true;
}

bool CTCPmap::PostRecv(MySocket *psocket)
{
    DWORD dwNumberOfBytesRecvd;
    DWORD dwFlag=0;
    psocket->type=NT_READ;
    WSABUF wb;
    wb.buf=psocket->szbufp;
    wb.len=sizeof(psocket->szbufp);
    if(WSARecv(psocket->socket,&wb,1,&dwNumberOfBytesRecvd,&dwFlag,&psocket->olp,0))

    {
        if(WSAGetLastError()!=WSA_IO_PENDING)
            return false;
    }


    return  true;

}


bool CTCPmap:: sendDate(SOCKET sockWaiter,char*szbuf,int  nLen)
{


    if(sockWaiter==INVALID_SOCKET||!szbuf||nLen<=0)
        return  false;
    if(send(sockWaiter,(char*)&nLen,sizeof(int ),0)<=0)
     return  false;
    if(send(sockWaiter,szbuf,nLen,0)<=0)
     return  false;

    int  nRecvNum;
    int nRecvNuumSIze=sizeof(szbuf);

    while(my_FlagQuit)
    {
        cin>>szbuf;

        send(sockWaiter,(char*)&nRecvNuumSIze,sizeof(int),0);
        send(sockWaiter,szbuf,sizeof(szbuf),0);

        //recv()
        nRecvNum= recv(sockWaiter,szbuf,sizeof(szbuf),0);
        if(sockWaiter>0)
        {
            cout<<"fuwuqi say:"<<szbuf<<endl;
        }



    }

   return true;

}

主函数

#include <QCoreApplication>
#include<winsock2.h>
#include<iostream>
#include"ctcpmap.h"
using namespace std;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    /*
     *1.选择种类  --WSAStartup();
     *2.雇店长  --创建套接字 socket();
     *3.找地 --绑定 --bind();
     *4.店长宣传 --监听 --listen();
     *5.店长接受客人 分配给服务员  --接受链接--accept();
     *6.客人与服务员 服务员等客人说话 --recv();
     *7.回复 --send();
     *8.下班--closesocket();
     *9.关门--WASCleanup();
     *
     */

    CTCPmap ctcp;
    if(ctcp.InitnewWork())
        cout<<"initnework sucess"<<endl;







    return a.exec();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值