參考資料: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);