网络:异步事件模型

相较于同步非阻塞模型

异步事件模型的特点:

效率要更高:由于异步通知

单线程处理多个套接字

异步事件模型:

向windows注册  当事件发生 将事件置为有信号

  步骤如下

1. 初始化网络

2.注册事件 :Eventselect(socket,fd_accept,事件)

3.创建处理事件的线程

while(1)

{
等多个事件信号  waitmulevents();

判断发生什么事件了

socket sockWaiter =accept();

向Windows注册(sockWaiter,fd_read,事件2);

}


完整代码如下

类声明:

#ifndef CTCPMAP_H
#define CTCPMAP_H
 
#include<list>
#include<winsock2.h>
#include<windows.h>
#include<iostream>
#include<map>
using namespace std;
#define MaxNum 64
struct DataInfo
{
    char* pszbuf;
    int  nPacksize;
    int  noffest;
};
 
class CTCPmap
{
public:
    //构造
    CTCPmap();
    //析构
    ~CTCPmap();
    //初始化网络
    bool InitnewWork();
    //销毁网络
    void DeleteWork();
    //线程函数
    static DWORD WINAPI ThreadEventSelect(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();
 
private:
    //监听者句柄
    SOCKET m_soclisten;
    //总线程池
    std::list<HANDLE> my_Threadpool;
    //服务者线程池
    std::list<SOCKET> my_ThreadWaiter;
    //线程结束标识
    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;
 
 
};
 
#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));
 
}
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;
 
    }
 
    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;
}
//将集合交给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:: 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、付费专栏及课程。

余额充值