棋牌遊戲demo 開發

參考資料:html

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

https://wenku.baidu.com/view/1b7ce142a8956bec0975e383.htmlc#

主要是學習epoll以後,想測試下epoll性能。已經實際項目中應該有的接口等問題。因此作了一個棋牌的demo.緩存

根據參考資料。和我的精力。省去網關,集成到中心服務器中。記錄下學習細節。服務器

初版非分佈式完成,第二版分佈式開發,太耗費時間在測試和服務客戶端2端的不一樣細節上,中斷,因此記錄下,以便有空繼續完成。網絡

1)大體模型。服務端c++,客戶端c#session

短連接登陸,-》分配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);//會發\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);
相關文章
相關標籤/搜索