• 棋牌游戏demo 开发


    参考资料:

    http://blog.csdn.net/zhangxy221/article/details/6595376

    https://wenku.baidu.com/view/1b7ce142a8956bec0975e383.html

    主要是学习epoll之后,想测试下epoll性能。已经实际项目中应该有的接口等问题。所以做了一个棋牌的demo.

    根据参考资料。和个人精力。省去网关,集成到中心服务器中。记录下学习细节。

    第一版非分布式完成,第二版分布式开发,太耗费时间在测试和服务客户端2端的不同细节上,中断,所以记录下,以便有空继续完成。

    1)大致模型。服务端c++,客户端c#

    短链接登录,-》分配sessionkey ,->得到key,进行长连接到网关(中心服),-》服务推送房间信息。-》用户占座,-》检测是否可以开始游戏-》对所有可以开始的游戏。进行分配gamekey

    ->通知游戏服初始化游戏数据-》游戏服发送数据给中心服,-》转发给客户-》客户出牌(或者发送聊天信息)-》中心服根据消息类型转发给游戏服,或者广播消息。-》游戏服根据出牌信息,或者超时后,由自动出牌方法,出牌。

    以此触发,推送最新玩家卡牌信息(通过到网关(中心服)给客户)。-》游戏结束通知中心服,更新玩家信息。和牌局结果。

    2)服务端的网络模块 epoll

    非阻塞,lt模式,由模块外保存缓存数据。 发送缓存满而失败的话,另外保存了发送失败缓存,监听模式由in转为out. 简单采用keeplive 来做心跳检测。

    3)数据的接受和发送。

    accept 之后,记录socket的key.发送检查KEY,以免数据发串。消息对于socket是顺序处理和发送。

    数据的序列化开始并没有,直接自己定义消息格式,后面采用protobuff. 

    4)线程模式,采用 一循环事件一线程,实现接口。而消息的处理指定线程数量,处理消息队列,简单的条件等待读模式。

    5)客户端的读写模式。

    为了熟悉异步,没有使用现成的异步调用,而是用新线程回调委托的方式,实现异步委托

     

    总结和改进:

    epoll的性能应该是没有问题。主要改进在缓存的处理上,可以采用环形缓冲区,

    由于已经是非阻塞多路复用,所以感觉不是很高的并发,整个服务端,几个线程应该是可以的。

    epoll一个线程。日志一个线程,主线程。固定个位数的线程,分批处理消息(每个线程按socket的接受缓存整条处理,以便消息的顺序,应该也可以对每条消息编号,)

    map 的使用还是要适当使用,log2 的性能比 vector 毕竟对于大数据是很高的提升。

    epollserver.h

    #ifndef LSU_EPOLLSERVER_H_INCLUDED
    #define LSU_EPOLLSERVER_H_INCLUDED
    
    #include <string>
    #include <map>
    #include <functional>
    #include <queue>
    #include <chrono>
    
    #include "lsu_iloopevent.h"
    #include "lsu_epoll_buff.h"
    
    extern "C"
    {
    
    namespace LSU_NET
    {
        enum enum_sock_recv
        {
            enum_sock_recv_ok=0,
            enum_sock_recv_close=-1,
            enum_sock_recv_error=-2,
        };
    
        enum enum_sock_Send
        {
            enum_sock_Send_ok=0,
            enum_sock_Send_block=-1,
            enum_sock_Send_error=-2,
        };
    
        class ScoketInfo
        {
        public:
            ScoketInfo()=default;
            ScoketInfo(int _fd);
            int fd;
            enum_sock_recv recvstatus;
            enum_sock_Send sendstatus;
            int createtime;
            int lastUpdate;
        };
        
        class EpollServer:public LSU_LOOPEVENT::IEventLoop
        {
        public:
            EpollServer(const std::string& _ip,int _port,int waitTimeout,
            const std::function<void(const std::string&)>& _OnNotice,
            const std::function<void(const std::vector<FDMsg>&)>& _onrecv,
            const std::function<std::vector<FDMsg>()>& _onsend,
            const std::function<void(const std::vector<FDMsg>&)>& _OnFailSend,
            const std::function<std::vector<FDMsg>(int fd)>& _OnCanSend
            );
            
            void Start();
            bool IsStart();
            void Close();
            bool IsClosed();
    
            
    
        private:
            int CreateListenFD();
            int AddEpollEvent(int _eventSourceFD,uint32_t _events);
            int DelEpollEvent(int _eventSourceFD);
            int MODEpollEvent(int _eventSourceFD,uint32_t _events);
            void TimeOutScoket();
            void DeleteLink(int fd);
        
        
            std::string serverIP;
            int serverPort;
            int waitTimeout;
            std::function<void(const std::string&)> OnNotice;
            std::function<void(const std::vector<FDMsg>&)> OnReceive;
            std::function<std::vector<FDMsg>()>OnSend;
            std::function<void(const std::vector<FDMsg>&)> OnFailSend;
            std::function<std::vector<FDMsg>(int fd)>OnCanSend;
            
            
            
            const int listenQueneCount=256;
            const static int MAX_EVENTS=64;
            const int recvBuffSize=1024*20;
            const int closeTimeout=20;
            const int kl_start=60;
            const int kl_times=6;
            const int kl_seconds=10;
            int listenFD;
            int epollManageFD;
            int localSockP[2];
            epoll_event ev_allEvents[MAX_EVENTS];
            std::map<int,ScoketInfo> allClientFD;
    
            bool shouldloop=true;
            bool stop=false;
            
            int loopTimePoint;
            
            const int to_close_ok=10;//接受到fin信号的超时
            const int to_close_block=60;//接受到fin信号的超时
            const int to_ok_ok=60*60;//客户端无任何动作的超市
            const int to_ok_block=30*60;
        };
    
    
        int Create_Bind_TCPv4_Socket(const std::string& _ip,int _port);
        int set_tcp_keepAlive(int fd,int start,int interval,int count);
    }
    
    }
    #endif // LSU_EPOLLSERVER_H_INCLUDED

    epollserver.cpp

    #include <stdio.h>//perror
    #include <fcntl.h>
    #include <unistd.h>//close.
    #include <time.h>
    #include <arpa/inet.h>//INET_PTON
    #include <chrono>
    #include <algorithm>
    #include <poll.h>
    #include <sys/epoll.h>
    #include <sys/errno.h>
    #include <ctime>
    #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET
    #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP
    #include <sys/errno.h>
    #include <netinet/tcp.h>
    #include <memory.h>
    #include <set>
    #include <exception>
    
    #include "lsu_epollserver.h"
    #include "lsu_time.h"
    #include "lsu_helper.h"
    
    using namespace std;
    using namespace LSU_HELPER;
    using namespace LSU_TIME;
    
    //多县城下面,必须开超时论寻.避免只有一个用户的尴尬.
    //用户发玩.消息放入 发送去.但是epoll,已经 素在在wait了.
    //因为是服务段,被动关闭.客户段,必须一部接受.才 不会 阻塞在 接受上.或者客户段,协议好,收到期望消息,就再次关闭,不必要在论需.
    //否则最好单独做个段链接模式,以便及时响应fin.
    
    namespace LSU_NET
    {
    ScoketInfo::ScoketInfo(int _fd)
    {
        fd=_fd;
        recvstatus=enum_sock_recv_ok;
        sendstatus=enum_sock_Send_ok;
        createtime=LSU_TIME::Getlocaltimeint();
        lastUpdate=LSU_TIME::Getlocaltimeint();
        
    }
    //ShowMsg,OnReceive,OnSend,OnFailSend,OnCanSend);
    EpollServer::EpollServer(const std::string& _ip,int _port,int _waitTimeout,
    const std::function<void(const std::string&)>& _OnNotice,
    const std::function<void(const std::vector<FDMsg>&)>& _onrecv,
    const std::function<std::vector<FDMsg>()>& _onsend,
    const std::function<void(const std::vector<FDMsg>&)>& _OnFailSend,
    const std::function<std::vector<FDMsg>(int fd)>& _OnCanSend
    ):
    serverIP(_ip),serverPort(_port),waitTimeout(_waitTimeout),OnNotice(_OnNotice),OnReceive(_onrecv),OnSend(_onsend),
    OnFailSend(_OnFailSend),OnCanSend(_OnCanSend)
    {
        localSockP[0]=-1;
        localSockP[1]=-1;
        bzero(ev_allEvents,sizeof(epoll_event)*MAX_EVENTS);
        loopTimePoint=Getlocaltimeint();
    }
    
    
    int EpollServer::CreateListenFD()
    {
        return Create_Bind_TCPv4_Socket(serverIP,serverPort);
    }
    
    int EpollServer::AddEpollEvent(int _eventSourceFD,uint32_t _events)
    {
        epoll_event ev_reginfo;
        bzero(&ev_reginfo,sizeof(ev_reginfo));
        ev_reginfo.events=_events;
        ev_reginfo.data.fd=_eventSourceFD;
        return epoll_ctl(epollManageFD,EPOLL_CTL_ADD,_eventSourceFD,&ev_reginfo);
    }
    
    
    int EpollServer::DelEpollEvent(int _eventSourceFD)
    {
        epoll_event ev_reginfo;//bugs:根据手册,早系统会有bug,所以还是传一个指针而不是0.
        bzero(&ev_reginfo,sizeof(ev_reginfo));
        ev_reginfo.data.fd=_eventSourceFD;
        return epoll_ctl(epollManageFD,EPOLL_CTL_DEL,_eventSourceFD,&ev_reginfo);
    }
    
    int EpollServer::MODEpollEvent(int _eventSourceFD,uint32_t _events)
    {
        epoll_event ev_reginfo;
        bzero(&ev_reginfo,sizeof(ev_reginfo));
        ev_reginfo.events=_events;
        ev_reginfo.data.fd=_eventSourceFD;
        return epoll_ctl(epollManageFD,EPOLL_CTL_MOD,_eventSourceFD,&ev_reginfo);
    }
    
    
    
    
    
    void EpollServer::Start()
    {
        //try{
        //Create Listen SOCKET
        int flag=0;
        listenFD=CreateListenFD();
        if(listenFD==-1)
        {
            OnNotice(string(strerror(errno)));
            return;
        }
    
        //listen socket
        flag=listen(listenFD,listenQueneCount);
        if(flag==-1)
        {
            OnNotice(string(strerror(errno)));
            return;
        }
    
        //create epoll server
        epollManageFD=epoll_create1(0);
        if(epollManageFD==-1)
        {
            close(listenFD);//并未多线程读取直接 close.
            OnNotice(string(strerror(errno)));
            return;
        }
    
        //create loacl socket
        flag= socketpair(AF_UNIX,SOCK_STREAM,0,localSockP);
        if(-1==flag)
        {
            close(listenFD);//并未多线程读取直接 close.
            close(epollManageFD);//并未多线程读取直接 close.
            OnNotice(string(strerror(errno)));
            return;
        }
    
        //注册监听描述符'
        flag= AddEpollEvent(listenFD,EPOLLIN);
        if(-1==flag)
        {
            close(listenFD);//并未多线程读取直接 close.
            close(epollManageFD);//并未多线程读取直接 close.
            OnNotice(string(strerror(errno)));
            return;
        }
    
        //注册本进程socket pair,0外部输入.1,由epoll监听.
        flag= AddEpollEvent(localSockP[1],EPOLLIN);
        if(-1==flag)
        {
            close(listenFD);//并未多线程读取直接 close.
            close(epollManageFD);//并未多线程读取直接 close.
            close(localSockP[0]);
            close(localSockP[1]);
            OnNotice(string(strerror(errno)));
            return;
        }
    
    
        OnNotice("Epoll start working!");
        int nfds=0;
        vector<FDMsg> recvData;//接受到的信息,批量发送。
        while(shouldloop)
        {
            
            //开始监听,1)监听描述符,2)本地socket 3)监听描述符所连接的新描述符
            nfds= epoll_wait(epollManageFD,ev_allEvents,MAX_EVENTS,waitTimeout);
    
            if(nfds==-1)
            {
                //需要重连接?
                OnNotice(ErrorDesc(errno,"wait -1"));
            }
            else
            {
                for(int i=0; i<nfds; ++i)
                {
                    //处理请求链接.
                    if(ev_allEvents[i].data.fd==listenFD&&ev_allEvents[i].events==EPOLLIN)
                    {
                        //需要非阻塞处理,提高效率?
                        int clientFD=accept(listenFD,0,0);
                        if(-1==clientFD)
                        {
                            OnNotice(ErrorDesc(errno,"accept error"));
                        }
                        else
                        {
                            //非阻塞,存活检测.
                            OnNotice(NormalDesc("accept ok.")+CastStr(clientFD));
                            int val=fcntl(clientFD,F_GETFL,0);
                            fcntl(clientFD,F_SETFL,val|O_NONBLOCK);
                            set_tcp_keepAlive(clientFD,kl_start,kl_times,kl_seconds);
    
                            flag=AddEpollEvent(clientFD,EPOLLIN);
                            if(-1==flag)
                            {
                                close(clientFD);
                                OnNotice(ErrorDesc(errno,"addevent"));
                            }
                            else
                            {
                                //如果存在必须先删除。可以不处理。已有数据,因为是多县城。处理不会彻底的。
                                //因为已经存在createtime这个全局的key.和每条信息以及fd关联
                                //消息的组合会匹配createtime.发送也会检测createtime.
                                //同一个fd编号。不可能会在一秒内重复建立。
                                if(allClientFD.find(clientFD)!=allClientFD.end())
                                {
                                    OnNotice(NormalDesc(CastStr(clientFD)+": exist,before reconnect .but not delete?"));
                                    allClientFD.erase(clientFD);
                                }
                                ScoketInfo si(clientFD);
                                allClientFD.insert(map<int,ScoketInfo>::value_type(clientFD,si));
                                OnNotice(NormalDesc("success conn"));
                            }
                        }
                    }
                    //处理进程内信号。
                    else if(ev_allEvents[i].data.fd==localSockP[1]&&ev_allEvents[i].events==EPOLLIN)
                    {
                        shouldloop=false;
                    }
                    //处理阻塞转发送事件
                    else if(ev_allEvents[i].events==EPOLLOUT)
                    {
                        int sendfd=ev_allEvents[i].data.fd;
                        vector<FDMsg> sendQueue= OnCanSend(sendfd);
                        if(allClientFD.find(sendfd)!=allClientFD.end())
                        {
                            for(int i=0;i<sendQueue.size();++i)
                            {
                                if(allClientFD[sendfd].createtime!=sendQueue[i].fdcreatetime)
                                {
                                    continue;
                                }
                                else
                                {
                                    flag=send(sendfd,sendQueue[i].data.c_str(),sendQueue[i].data.size(),0);//会发?
                                    if(-1==flag)
                                    {
                                        //要不客户不通。短线。。断掉。
                                        //要不素赛。素赛的可能是。用户不接受。但这个发送非常频繁。在转向关注发送期间的数据,居然填满缓存。
                                        DeleteLink(sendfd);
                                        OnNotice(NormalDesc("resend fail just colse it"+CastStr(sendfd)));
                                        break;
                                    }
                                }
                            }
                            //if all ok.
                            OnNotice(NormalDesc("resend ok"));
                            allClientFD[sendfd].sendstatus=enum_sock_Send_ok;
                            allClientFD[sendfd].lastUpdate=Getlocaltimeint();
                            if(allClientFD[sendfd].recvstatus!=enum_sock_recv_close)
                            {
                                OnNotice(NormalDesc("resend ok and start to recv."+CastStr(sendfd)));
                                MODEpollEvent(sendfd,EPOLLIN);
                            }
                        }
                        else
                        {
                            DeleteLink(sendfd);
                            OnNotice(NormalDesc("set delete.but event exist?"+CastStr(sendfd)));
                        }
                    }
                    //处理接受的数据.
                    else
                    {
                        int myEventFD=ev_allEvents[i].data.fd;
                        if(allClientFD.find(myEventFD)==allClientFD.end())
                        {
                            ScoketInfo si(myEventFD);
                            allClientFD.insert(map<int,ScoketInfo>::value_type(myEventFD,si));
                            OnNotice(NormalDesc("noway,a unconnected client?"));
                        }
                        else
                        {
                            allClientFD[myEventFD].lastUpdate=Getlocaltimeint();
                        }
    
    
                        char recvChar[recvBuffSize];
                        string recvMsg;
                        for(;;)
                        {
                            bzero(recvChar,recvBuffSize);
                            int reclen= recv(myEventFD,recvChar,recvBuffSize,0);
    
                            if(reclen>0)
                            {
                                recvMsg+=string(recvChar,reclen);
                            }
                            //用户至少半关闭链接
                            else if(reclen==0)
                            {
                                allClientFD[myEventFD].recvstatus=enum_sock_recv_close;
                                break;
                            }
                            else if(reclen==-1)
                            {
                                //数据收完
                                if(errno==EAGAIN || errno==EWOULDBLOCK)
                                {
                                    allClientFD[myEventFD].recvstatus=enum_sock_recv_ok;
                                    break;
                                }
                                else if(errno==EINTR)
                                {
                                    continue;
                                }
                                //客户断网,或者崩溃(keep alive 会自动请求进行到这里。)
                                else
                                {
                                    recvMsg="";
                                    allClientFD[myEventFD].recvstatus=enum_sock_recv_error;
                                    break;
                                }
                            }
                        }
    
                        if(allClientFD[myEventFD].recvstatus==enum_sock_recv_ok)
                        {
                            if(recvMsg!="")
                            {
                                FDMsg tempdb;
                                tempdb.data=recvMsg;
                                tempdb.fd=myEventFD;
                                tempdb.fdcreatetime=allClientFD[myEventFD].createtime;
                                recvData.push_back(tempdb);
                            }
                            allClientFD[myEventFD].lastUpdate=Getlocaltimeint();
                            OnNotice(NormalDesc(CastStr(myEventFD)+": recv ok.data:"+recvMsg));
                        }
                        else if(allClientFD[myEventFD].recvstatus==enum_sock_recv_close)
                        {
                            if(recvMsg!="")
                            {
                                FDMsg tempdb;
                                tempdb.data=recvMsg;
                                tempdb.fd=myEventFD;
                                tempdb.fdcreatetime=allClientFD[myEventFD].createtime;
                                recvData.push_back(tempdb);
                            }
                            DelEpollEvent(myEventFD);
                            allClientFD[myEventFD].lastUpdate=Getlocaltimeint();
                            OnNotice(NormalDesc(CastStr(myEventFD)+": recv ok.cliete want to close.data:"+recvMsg));
                        }
                        else if(allClientFD[myEventFD].recvstatus==enum_sock_recv_error)
                        {
                            DeleteLink(myEventFD);
                            OnNotice(ErrorDesc(errno,CastStr(myEventFD)+": recv."));
                        }
                    }
                }
            }
            if(recvData.size()>0)
            {
                OnReceive(recvData);
                OnNotice(NormalDesc("push recv to buff.count of recv size:"+CastStr(recvData.size())));
                recvData.clear();
                
            }
    
            //处理发送.
            vector<FDMsg> SendQueue=OnSend();
            
            if(SendQueue.size()>0)
            {
                OnNotice(NormalDesc("start send.size:"+CastStr(SendQueue.size())));
                vector<FDMsg> failSend;
                for(size_t i=0; i<SendQueue.size(); ++i)
                {
                    int sendfd=SendQueue[i].fd;
                    int sendfdcreatet=SendQueue[i].fdcreatetime;
                    if(allClientFD.find(sendfd)!=allClientFD.end())
                    {
                        //时间戳不对。
                        if(sendfdcreatet!=allClientFD[sendfd].createtime)
                        {
                            OnNotice(NormalDesc(CastStr(sendfd)+": error createtime.do't send."));
                            continue;
                        }
                        
                        //如果之前此链接有错误。直接跳过。
                        if(allClientFD[sendfd].sendstatus==enum_sock_Send_error)
                        {
                            OnNotice(NormalDesc(CastStr(sendfd)+": before has error.skip and discard."));
                            continue;
                        }
                        //如果是阻塞链接。放入阻塞队伍。进行下过。以免浪费时间,或者中途又可以,导致逻辑更复杂。干脆直接关注发送事件。
                        else if(allClientFD[sendfd].sendstatus==enum_sock_Send_block)
                        {
                            failSend.push_back(SendQueue[i]);
                            OnNotice(NormalDesc(CastStr(sendfd)+": before has blocked.skip and put failqueue."));
                            continue;
                        }
    
                        flag=send(sendfd,SendQueue[i].data.c_str(),SendQueue[i].data.size(),0);//会发?
                        if(-1==flag)
                        {
                            if(errno==EINTR || errno==EAGAIN || errno==EWOULDBLOCK)
                            {
                                allClientFD[sendfd].sendstatus=enum_sock_Send_block;
                                allClientFD[sendfd].lastUpdate=Getlocaltimeint();
                                MODEpollEvent(SendQueue[i].fd,EPOLLOUT);//关注接收,转为关注发送,不再进行接收了.发都发不出去.不接收.
                                failSend.push_back(SendQueue[i]);
                                OnNotice(NormalDesc(CastStr(sendfd)+": send block.skip and put failqueue."));
                            }
                            else
                            {
                                //allClientFD[sendfd].sendstatus=enum_sock_Send_error;
                                //allClientFD[sendfd].lastUpdate=Getlocaltimeint();
                                DeleteLink(sendfd);
                                OnNotice(ErrorDesc(errno,CastStr(sendfd)+": send error.close55 it"));
                            }
                        }
                        else
                        {
                            allClientFD[sendfd].sendstatus=enum_sock_Send_ok;
                            allClientFD[sendfd].lastUpdate=Getlocaltimeint();
                            OnNotice(NormalDesc(CastStr(sendfd)+": send ok.data:"+string(SendQueue[i].data.c_str(),SendQueue[i].data.size())));
                        }
                    }
                    else
                    {
                        OnNotice(NormalDesc(CastStr(sendfd)+": not in set.do't send."));
                    }
                    
                    //如果开启段链接模式.那么直接
                }
    
                if(failSend.size()>0)
                {
                    OnFailSend(failSend);
                }
            }
            
            //超时链接处理。
            int looptimenow=Getlocaltimeint();
            if(looptimenow-loopTimePoint>15)
            {
                
                TimeOutScoket();
                loopTimePoint=looptimenow;
            }
        }
        stop=true;
        OnNotice("Server closed!");
        //通知阻塞在读取接收缓存上的线程,重新检测状态
        OnReceive(vector<FDMsg>());
        close(epollManageFD);
    }
    
    
    bool EpollServer::IsStart()
    {
        return shouldloop;
    }
    
    void EpollServer::Close()
    {
        send(localSockP[0],"close",5,0);
    }
    
    bool EpollServer::IsClosed()
    {
        return stop;
    }
    
    void EpollServer::DeleteLink(int fd)
    {
        DelEpollEvent(fd);
        shutdown(fd,SHUT_RDWR);//回应客户端,关闭tcp链接.
        close(fd);//本地清除文件描述符和socket 的资源.
        allClientFD.erase(fd);
        OnNotice(NormalDesc("deletelink it"+CastStr(fd)));
    }
    
    //这里明显效率可以改善 ,可以加几个链表。分别表示不同的超市类型链接。并按时间排序。检测到表的某个地方就可以。
    //而不是循环每个链接。但是要做数据间的同步。就简单点吧.
    void EpollServer::TimeOutScoket()
    {
        OnNotice("start loop");
        int timenow=Getlocaltimeint();
        auto it=allClientFD.begin();
        vector<int> delfd;
        for(it;it!=allClientFD.end();++it)
        {
            ScoketInfo sockinfo=it->second;
            int socketfd=sockinfo.fd;
            if(sockinfo.recvstatus==enum_sock_recv_close&&sockinfo.sendstatus==enum_sock_Send_ok&&timenow-sockinfo.lastUpdate>to_close_ok)
            {
                OnNotice("close ok"+CastStr(socketfd));
                delfd.push_back(socketfd);
            }
            else if(sockinfo.recvstatus==enum_sock_recv_close&&sockinfo.sendstatus==enum_sock_Send_block&&timenow-sockinfo.lastUpdate>to_close_block)
            {
                OnNotice("close block"+CastStr(socketfd));
                delfd.push_back(socketfd);
            }
            else if(sockinfo.recvstatus==enum_sock_recv_ok&&sockinfo.sendstatus==enum_sock_Send_ok&&timenow-sockinfo.lastUpdate>to_ok_ok)
            {
                OnNotice("ok ok"+CastStr(timenow)+CastStr(sockinfo.lastUpdate));
                delfd.push_back(socketfd);
            }
            else if(sockinfo.recvstatus==enum_sock_recv_ok&&sockinfo.sendstatus==enum_sock_Send_block&&timenow-sockinfo.lastUpdate>to_ok_block)
            {
                OnNotice("ok block");
                delfd.push_back(socketfd);
            }
        }
        for(int i=0;i<delfd.size();++i)
        {
            DeleteLink(delfd[i]);
        }
    }
    
    
    //global fun. fail: return -1;
    int Create_Bind_TCPv4_Socket(const std::string& _ip,int _port)
    {
        int socketFD=-1;
        socketFD=socket(PF_INET,SOCK_STREAM,IPPROTO_IP);
        if(socketFD<0)
        {
            return -1;
        }
    
        sockaddr_in mySockAddr;
        bzero(&mySockAddr,sizeof(mySockAddr));
        inet_pton(AF_INET,_ip.c_str(),&mySockAddr.sin_addr);
        mySockAddr.sin_family=AF_INET;
        mySockAddr.sin_port=htons(_port);
        int flag=bind(socketFD,(sockaddr*)&mySockAddr,sizeof(mySockAddr));
        if(flag==-1)
        {
            return -1;
        }
    
        return socketFD;
    }
    
    
    
    int set_tcp_keepAlive(int fd,int start,int interval,int count)
    {
        int keepAlive = 1;
        if (fd < 0 || start < 0 || interval < 0 || count < 0) return -1;
    
        if(setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1)
        {
            return -1;
        }
    
        if(setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,(void *)&start,sizeof(start)) == -1)
        {
            return -1;
        }
    
        if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,(void *)&interval,sizeof(interval)) == -1)
        {
            return -1;
        }
    
        if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,(void *)&count,sizeof(count)) == -1)
        {
            return -1;
        }
        return 0;
    }
    
    }
    
    //4)send_ok.        s_ok  ,1)close:yes,block:yes=>s_ok   2)close:yes,block:no => s_ok 3) colse:no,block:yes=>s_ok. e_in. 4)close:no,block:no=>s_ok

    proto 消息格式。

    package GAMEMSG;
    enum MsgType
    {
     enum_Login_Request  = 10001;
     enum_Login_Response  = 10002;
     enum_Logout_Request  = 10003;
     enum_Reflesh_Rooms_Request=10004;
     enum_Reflesh_Rooms_Response=10005;
     enum_Stool_Info_Push=10006;
     enum_SitDown_Request=10007;
     enum_Game_Info_Push=10008;
     enum_SendCards_Request=10009;
     enum_Chat_Request=10010;
     enum_Chat_Push=10011;
     enum_GameOver_Push=10012;
    }
    
    message Message
    {
     required MsgType Msg_Type = 1;
     required MsgContent Msg_Content=2;
    }
    
    message MsgContent
    {
     optional LoginRequest Login_Request = 1;
     optional LoginResponse Login_Response = 2;
     optional LogoutRequest Logout_Request=3;
     optional RefleshRoomsRequest Reflesh_Rooms_Request = 4;
     optional RefleshRoomsResponse Reflesh_Rooms_Response = 5;
     optional StoolInfoPush Stool_Info_Push=6;
     optional SitDownRequest SitDown_Request=7;
     optional GameInfoPush Game_Info_Push=8;
     optional SendCardsRequest SendCards_Request=9;
     optional ChatRequest Chat_Request=10;
     optional ChatPush Chat_Push=11;
     optional GameOverPush GameOver_Push=12;
    
    }
    
    message LoginRequest
    {
     required bytes User_Name = 1;
     required bytes PassWord = 2;
    }
    
    message LoginResponse
    {
     required bytes User_Name = 1;
     required bytes User_Key = 2;
     required int32 Check_Ret=3;
    }
    
    message LogoutRequest
    {
     required bytes User_name=1;
     required bytes User_key=2;
    }
    
    message RefleshRoomsRequest
    {
     required bytes Check_key=1;
    }
    
    message RefleshRoomsResponse
    {
     repeated RoomInfo Room_Info=1;
    }
    
    message StoolInfoPush
    {
     required int32 Room_id=1;
     repeated bytes Player_name=2;
    }
    
    message SitDownRequest
    {
     required bytes User_name=1;
     required bytes User_key=2;
     required int32 Room_id=3;
     required int32 Desk_id=4;
     required int32 Stool_id=5;
    }
    
    message SendCardsRequest
    {
     required bytes User_name=1;
     required bytes User_key=2;
     required bytes Game_key=3;
     repeated int32 Send_cards=4;
    }
    
    message GameInfoPush
    {
     required bytes Game_key=1;
     required int32 Stool_id=2;
     required int32 Who_turn=3;
     required int32 Who_pre=4;
     repeated CardInfo My_Cards=5;
     repeated CardInfo Pre_Cards=6;
     repeated bytes Player_list=7;
     required int32 Left_Second=8;
    }
    
    message ChatRequest
    {
     required bytes Game_key=1;
     required bytes Player_Name=2;
     required bytes Chat_msg=3;
    }
    
    
    message ChatPush
    {
     required bytes Game_key=1;
     required bytes Player_Name=2;
     required bytes Chat_msg=3;
    }
    
    
    message GameOverPush
    {
     required bytes Game_key=1;
     required bytes Winer_Name=2;
    }
    
    message RoomInfo
    {
     required int32 Room_id=1;
     required bytes Room_name=2;
     required int32 Desk_count=3;
     required int32 Stool_count=4;
     required int32 Room_type=5;
    }
    
    message CardInfo
    {
     required int32 Card_No=1;
     required int32 Card_Color=2;
     required int32 Card_Used=3;
    }

    c++发送消息样例

    GAMEMSG::Message msg;
            msg.set_msg_type(MsgType::enum_Reflesh_Rooms_Response);
            
            GAMEMSG::MsgContent *pcontent=msg.mutable_msg_content();
            GAMEMSG::RefleshRoomsResponse *prrr=  pcontent->mutable_reflesh_rooms_response();
            
            MODEL::RoomDAL dal_room;
            map<int,MODEL::room> allrooms= dal_room.GetList();
            for(size_t i=0;i<allrooms.size();++i)
            {
                GAMEMSG::RoomInfo *oneRoom=  prrr->add_room_info();
                oneRoom->set_room_id(allrooms[i].room_id);
                oneRoom->set_room_name(allrooms[i].room_Name);
                oneRoom->set_room_type(allrooms[i].room_type);
                oneRoom->set_desk_count(allrooms[i].desk_count);
                oneRoom->set_stool_count(allrooms[i].stool_count);
            }
            ret=PortoBufHandle::AddSize(msg);

    c++处理消息了样例

    vector<shared_ptr<GAMEMSG::Message>> doingMsgs= PortoBufHandle::ParseData(msg,pendMsg);
                if(pendMsg.size()>0)
                {
                    EchoFun(LSU_HELPER::CastStr(fd)+"pending:"+pendMsg);
                    GlobalBuff.UpdatePendBuff(fd,createtime,pendMsg);
                }
                
                for(size_t i=0;i<doingMsgs.size();++i)
                {
                    GAMEMSG::MsgType mType=doingMsgs[i]->msg_type();
                    GAMEMSG::MsgContent mContent=GAMEMSG::MsgContent();
                    mContent=doingMsgs[i]->msg_content();
                    
                    string protomsg;
                    if(mType==GAMEMSG::MsgType::enum_Reflesh_Rooms_Request)
                    {
                        GAMEMSG::RefleshRoomsRequest realmsg=mContent.reflesh_rooms_request();
                        protomsg= RoomProcess::room_Request(realmsg);
                    }
                    else if(mType==GAMEMSG::MsgType::enum_Login_Request)

    c#下的protobuf 的书写样式。

    GAMEMSG.Message msg=GAMEMSG.Message.CreateBuilder()
                    .SetMsgType(GAMEMSG.MsgType.enum_Login_Request)
                    .SetMsgContent(GAMEMSG.MsgContent.CreateBuilder()
                        .SetLoginRequest(GAMEMSG.LoginRequest.CreateBuilder()
                            .SetUserName(ByteString.CopyFromUtf8(name))
                            .SetPassWord(ByteString.CopyFromUtf8(psw))
                            .Build())
                        .Build())
                    .Build();
    
                byte[] bytes = msg.ToByteArray();
                byte[] sendbyte= BLL2.msgHelper.AddSize(bytes);
  • 相关阅读:
    三列布局_左右绝对定位_中间适应
    三列布局_左右固定_中间自适应
    两列布局_左右二侧_绝对定位
    二列布局_左右固定_自己撑开父级块
    两列布局_右侧固定_左侧自适应
    两列布局_左侧固定_右侧自适应
    单列布局_宽度自适应_内容居中
    单列布局_上中下等宽
    聊一聊Unity协程背后的实现原理
    发火箭和做游戏有什么共通点?
  • 原文地址:https://www.cnblogs.com/lsfv/p/6442912.html
Copyright © 2020-2023  润新知