棋牌游戏demo 开发

时间:2022-12-22 03:34:14

参考资料:

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

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

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

棋牌游戏demo 开发

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);//会发\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);//会发\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);