受欢迎的博客标签

棋牌游戏开发大致模型,服务端c++,客户端c#

Published
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);//会发\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);复制代码.