参考资料:
http://blog.csdn.net/zhangxy221/article/details/6595376
https://wenku.baidu.com/view/1b7ce142a8956bec0975e383.html
主要是学习epoll之后,想测试下epoll性能。已经实际项目中应该有的接口等问题。所以做了一个棋牌的demo.
根据参考资料。和个人精力。省去网关,集成到中心服务器中。记录下学习细节。
第一版非分布式完成,第二版分布式开发,太耗费时间在测试和服务客户端2端的不同细节上,中断,所以记录下,以便有空继续完成。
1)大致模型。服务端c++,客户端c#
短链接登录,-》分配sessionkey ,->得到key,进行长连接到网关(中心服),-》服务推送房间信息。-》用户占座,-》检测是否可以开始游戏-》对所有可以开始的游戏。进行分配gamekey
->通知游戏服初始化游戏数据-》游戏服发送数据给中心服,-》转发给客户-》客户出牌(或者发送聊天信息)-》中心服根据消息类型转发给游戏服,或者广播消息。-》游戏服根据出牌信息,或者超时后,由自动出牌方法,出牌。
以此触发,推送最新玩家卡牌信息(通过到网关(中心服)给客户)。-》游戏结束通知中心服,更新玩家信息。和牌局结果。
2)服务端的网络模块 epoll
非阻塞,lt模式,由模块外保存缓存数据。 发送缓存满而失败的话,另外保存了发送失败缓存,监听模式由in转为out. 简单采用keeplive 来做心跳检测。
3)数据的接受和发送。
accept 之后,记录socket的key.发送检查KEY,以免数据发串。消息对于socket是顺序处理和发送。
数据的序列化开始并没有,直接自己定义消息格式,后面采用protobuff.
4)线程模式,采用 一循环事件一线程,实现接口。而消息的处理指定线程数量,处理消息队列,简单的条件等待读模式。
5)客户端的读写模式。
为了熟悉异步,没有使用现成的异步调用,而是用新线程回调委托的方式,实现异步委托
总结和改进:
epoll的性能应该是没有问题。主要改进在缓存的处理上,可以采用环形缓冲区,
由于已经是非阻塞多路复用,所以感觉不是很高的并发,整个服务端,几个线程应该是可以的。
epoll一个线程。日志一个线程,主线程。固定个位数的线程,分批处理消息(每个线程按socket的接受缓存整条处理,以便消息的顺序,应该也可以对每条消息编号,)
map 的使用还是要适当使用,log2 的性能比 vector 毕竟对于大数据是很高的提升。
epollserver.h
#ifndef LSU_EPOLLSERVER_H_INCLUDED #define LSU_EPOLLSERVER_H_INCLUDED #include <string> #include <map> #include <functional> #include <queue> #include <chrono> #include "lsu_iloopevent.h" #include "lsu_epoll_buff.h" extern "C" { namespace LSU_NET { enum enum_sock_recv { enum_sock_recv_ok=0, enum_sock_recv_close=-1, enum_sock_recv_error=-2, }; enum enum_sock_Send { enum_sock_Send_ok=0, enum_sock_Send_block=-1, enum_sock_Send_error=-2, }; class ScoketInfo { public: ScoketInfo()=default; ScoketInfo(int _fd); int fd; enum_sock_recv recvstatus; enum_sock_Send sendstatus; int createtime; int lastUpdate; }; class EpollServer:public LSU_LOOPEVENT::IEventLoop { public: EpollServer(const std::string& _ip,int _port,int waitTimeout, const std::function<void(const std::string&)>& _OnNotice, const std::function<void(const std::vector<FDMsg>&)>& _onrecv, const std::function<std::vector<FDMsg>()>& _onsend, const std::function<void(const std::vector<FDMsg>&)>& _OnFailSend, const std::function<std::vector<FDMsg>(int fd)>& _OnCanSend ); void Start(); bool IsStart(); void Close(); bool IsClosed(); private: int CreateListenFD(); int AddEpollEvent(int _eventSourceFD,uint32_t _events); int DelEpollEvent(int _eventSourceFD); int MODEpollEvent(int _eventSourceFD,uint32_t _events); void TimeOutScoket(); void DeleteLink(int fd); std::string serverIP; int serverPort; int waitTimeout; std::function<void(const std::string&)> OnNotice; std::function<void(const std::vector<FDMsg>&)> OnReceive; std::function<std::vector<FDMsg>()>OnSend; std::function<void(const std::vector<FDMsg>&)> OnFailSend; std::function<std::vector<FDMsg>(int fd)>OnCanSend; const int listenQueneCount=256; const static int MAX_EVENTS=64; const int recvBuffSize=1024*20; const int closeTimeout=20; const int kl_start=60; const int kl_times=6; const int kl_seconds=10; int listenFD; int epollManageFD; int localSockP[2]; epoll_event ev_allEvents[MAX_EVENTS]; std::map<int,ScoketInfo> allClientFD; bool shouldloop=true; bool stop=false; int loopTimePoint; const int to_close_ok=10;//接受到fin信号的超时 const int to_close_block=60;//接受到fin信号的超时 const int to_ok_ok=60*60;//客户端无任何动作的超市 const int to_ok_block=30*60; }; int Create_Bind_TCPv4_Socket(const std::string& _ip,int _port); int set_tcp_keepAlive(int fd,int start,int interval,int count); } } #endif // LSU_EPOLLSERVER_H_INCLUDED
epollserver.cpp
#include <stdio.h>//perror #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <arpa/inet.h>//INET_PTON #include <chrono> #include <algorithm> #include <poll.h> #include <sys/epoll.h> #include <sys/errno.h> #include <ctime> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <netinet/tcp.h> #include <memory.h> #include <set> #include <exception> #include "lsu_epollserver.h" #include "lsu_time.h" #include "lsu_helper.h" using namespace std; using namespace LSU_HELPER; using namespace LSU_TIME; //多县城下面,必须开超时论寻.避免只有一个用户的尴尬. //用户发玩.消息放入 发送去.但是epoll,已经 素在在wait了. //因为是服务段,被动关闭.客户段,必须一部接受.才 不会 阻塞在 接受上.或者客户段,协议好,收到期望消息,就再次关闭,不必要在论需. //否则最好单独做个段链接模式,以便及时响应fin. namespace LSU_NET { ScoketInfo::ScoketInfo(int _fd) { fd=_fd; recvstatus=enum_sock_recv_ok; sendstatus=enum_sock_Send_ok; createtime=LSU_TIME::Getlocaltimeint(); lastUpdate=LSU_TIME::Getlocaltimeint(); } //ShowMsg,OnReceive,OnSend,OnFailSend,OnCanSend); EpollServer::EpollServer(const std::string& _ip,int _port,int _waitTimeout, const std::function<void(const std::string&)>& _OnNotice, const std::function<void(const std::vector<FDMsg>&)>& _onrecv, const std::function<std::vector<FDMsg>()>& _onsend, const std::function<void(const std::vector<FDMsg>&)>& _OnFailSend, const std::function<std::vector<FDMsg>(int fd)>& _OnCanSend ): serverIP(_ip),serverPort(_port),waitTimeout(_waitTimeout),OnNotice(_OnNotice),OnReceive(_onrecv),OnSend(_onsend), OnFailSend(_OnFailSend),OnCanSend(_OnCanSend) { localSockP[0]=-1; localSockP[1]=-1; bzero(ev_allEvents,sizeof(epoll_event)*MAX_EVENTS); loopTimePoint=Getlocaltimeint(); } int EpollServer::CreateListenFD() { return Create_Bind_TCPv4_Socket(serverIP,serverPort); } int EpollServer::AddEpollEvent(int _eventSourceFD,uint32_t _events) { epoll_event ev_reginfo; bzero(&ev_reginfo,sizeof(ev_reginfo)); ev_reginfo.events=_events; ev_reginfo.data.fd=_eventSourceFD; return epoll_ctl(epollManageFD,EPOLL_CTL_ADD,_eventSourceFD,&ev_reginfo); } int EpollServer::DelEpollEvent(int _eventSourceFD) { epoll_event ev_reginfo;//bugs:根据手册,早系统会有bug,所以还是传一个指针而不是0. bzero(&ev_reginfo,sizeof(ev_reginfo)); ev_reginfo.data.fd=_eventSourceFD; return epoll_ctl(epollManageFD,EPOLL_CTL_DEL,_eventSourceFD,&ev_reginfo); } int EpollServer::MODEpollEvent(int _eventSourceFD,uint32_t _events) { epoll_event ev_reginfo; bzero(&ev_reginfo,sizeof(ev_reginfo)); ev_reginfo.events=_events; ev_reginfo.data.fd=_eventSourceFD; return epoll_ctl(epollManageFD,EPOLL_CTL_MOD,_eventSourceFD,&ev_reginfo); } void EpollServer::Start() { //try{ //Create Listen SOCKET int flag=0; listenFD=CreateListenFD(); if(listenFD==-1) { OnNotice(string(strerror(errno))); return; } //listen socket flag=listen(listenFD,listenQueneCount); if(flag==-1) { OnNotice(string(strerror(errno))); return; } //create epoll server epollManageFD=epoll_create1(0); if(epollManageFD==-1) { close(listenFD);//并未多线程读取直接 close. OnNotice(string(strerror(errno))); return; } //create loacl socket flag= socketpair(AF_UNIX,SOCK_STREAM,0,localSockP); if(-1==flag) { close(listenFD);//并未多线程读取直接 close. close(epollManageFD);//并未多线程读取直接 close. OnNotice(string(strerror(errno))); return; } //注册监听描述符' flag= AddEpollEvent(listenFD,EPOLLIN); if(-1==flag) { close(listenFD);//并未多线程读取直接 close. close(epollManageFD);//并未多线程读取直接 close. OnNotice(string(strerror(errno))); return; } //注册本进程socket pair,0外部输入.1,由epoll监听. flag= AddEpollEvent(localSockP[1],EPOLLIN); if(-1==flag) { close(listenFD);//并未多线程读取直接 close. close(epollManageFD);//并未多线程读取直接 close. close(localSockP[0]); close(localSockP[1]); OnNotice(string(strerror(errno))); return; } OnNotice("Epoll start working!"); int nfds=0; vector<FDMsg> recvData;//接受到的信息,批量发送。 while(shouldloop) { //开始监听,1)监听描述符,2)本地socket 3)监听描述符所连接的新描述符 nfds= epoll_wait(epollManageFD,ev_allEvents,MAX_EVENTS,waitTimeout); if(nfds==-1) { //需要重连接? OnNotice(ErrorDesc(errno,"wait -1")); } else { for(int i=0; i<nfds; ++i) { //处理请求链接. if(ev_allEvents[i].data.fd==listenFD&&ev_allEvents[i].events==EPOLLIN) { //需要非阻塞处理,提高效率? int clientFD=accept(listenFD,0,0); if(-1==clientFD) { OnNotice(ErrorDesc(errno,"accept error")); } else { //非阻塞,存活检测. OnNotice(NormalDesc("accept ok.")+CastStr(clientFD)); int val=fcntl(clientFD,F_GETFL,0); fcntl(clientFD,F_SETFL,val|O_NONBLOCK); set_tcp_keepAlive(clientFD,kl_start,kl_times,kl_seconds); flag=AddEpollEvent(clientFD,EPOLLIN); if(-1==flag) { close(clientFD); OnNotice(ErrorDesc(errno,"addevent")); } else { //如果存在必须先删除。可以不处理。已有数据,因为是多县城。处理不会彻底的。 //因为已经存在createtime这个全局的key.和每条信息以及fd关联 //消息的组合会匹配createtime.发送也会检测createtime. //同一个fd编号。不可能会在一秒内重复建立。 if(allClientFD.find(clientFD)!=allClientFD.end()) { OnNotice(NormalDesc(CastStr(clientFD)+": exist,before reconnect .but not delete?")); allClientFD.erase(clientFD); } ScoketInfo si(clientFD); allClientFD.insert(map<int,ScoketInfo>::value_type(clientFD,si)); OnNotice(NormalDesc("success conn")); } } } //处理进程内信号。 else if(ev_allEvents[i].data.fd==localSockP[1]&&ev_allEvents[i].events==EPOLLIN) { shouldloop=false; } //处理阻塞转发送事件 else if(ev_allEvents[i].events==EPOLLOUT) { int sendfd=ev_allEvents[i].data.fd; vector<FDMsg> sendQueue= OnCanSend(sendfd); if(allClientFD.find(sendfd)!=allClientFD.end()) { for(int i=0;i<sendQueue.size();++i) { if(allClientFD[sendfd].createtime!=sendQueue[i].fdcreatetime) { continue; } else { flag=send(sendfd,sendQueue[i].data.c_str(),sendQueue[i].data.size(),0);//会发\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);