加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

跟着BOY 学习COCOS2D-X 网络篇---强联网(采用技术 BSD SOCKET+

发布时间:2020-12-14 19:48:34 所属栏目:百科 来源:网络整理
导读:http://blog.csdn.net/zzhboy/article/details/9878941 分类:cocos2d-x 编译cocos2d-x SOCKETgoogle protobuf 2013-08-10 16:41 9314人阅读 评论(36) 收藏 举报 cocos2d-x 多线程 google protobuf socket 网络 如果按照上面的一讲 你如果把环境搭建好了,下
http://blog.csdn.net/zzhboy/article/details/9878941
分类:cocos2d-x 编译cocos2d-x SOCKETgoogle protobuf 9314人阅读 评论(36) 收藏 举报
cocos2d-x 多线程 google protobuf socket 网络

如果按照上面的一讲 你如果把环境搭建好了,下面我们就正式开始客户端的搭建 首先我献给大家画一张我的客户端实现的流程图

我PS 画的大家不要见怪啊 不过流程就是这样的

搭建看到我上面的框架图的时候 就知道我的大概设计思路,

boy 在这里强调一点 这个是用异步的结构实现 其中线程类 我是参照java 里面的方法。

好了废话不多 首先先上 BSD SOCKET 这个核心类

[cpp] view plain copy
  1. /*
  2. *definefileaboutportablesocketclass.
  3. *description:thissockissuitbothwindowsandlinux
  4. *design:odison
  5. *e-mail:odison@126.com>
  6. *
  7. */
  8. #ifndef_ODSOCKET_H_
  9. #define_ODSOCKET_H_
  10. #ifdefWIN32
  11. #include<winsock2.h>
  12. typedefintsocklen_t;
  13. #else
  14. #include<sys/socket.h>
  15. #include<netinet/in.h>
  16. #include<netdb.h>
  17. #include<fcntl.h>
  18. #include<unistd.h>
  19. #include<sys/stat.h>
  20. #include<sys/types.h>
  21. #include<arpa/inet.h>
  22. intSOCKET;
  23. //#pragmaregiondefinewin32constvariableinlinux
  24. #defineINVALID_SOCKET-1
  25. #defineSOCKET_ERROR-1
  26. //#pragmaendregion
  27. #endif
  28. classODSocket{
  29. public:
  30. ODSocket(SOCKETsock=INVALID_SOCKET);
  31. ~ODSocket();
  32. //Createsocketobjectforsnd/recvdata
  33. boolCreate(intaf,inttype,87); font-weight:bold; background-color:inherit">intprotocol=0);
  34. //Connectsocket
  35. boolConnect(constchar*ip,unsignedshortport);
  36. //#regionserver
  37. //Bindsocket
  38. boolBind(unsignedshortport);
  39. //Listensocket
  40. boolListen(intbacklog=5);
  41. //Acceptsocket
  42. boolAccept(ODSocket&s,87); font-weight:bold; background-color:inherit">char*fromip=NULL);
  43. //#endregion
  44. intSelect();
  45. //Sendsocket
  46. intSend(char*buf,87); font-weight:bold; background-color:inherit">intlen,87); font-weight:bold; background-color:inherit">intflags=0);
  47. //Recvsocket
  48. intRecv(intflags=0);
  49. //Closesocket
  50. intClose();
  51. //Geterrno
  52. intGetError();
  53. //#pragmaregionjustforwin32
  54. //InitwinsockDLL
  55. staticintInit();
  56. //CleanwinsockDLL
  57. intClean();
  58. //#pragmaendregion
  59. //Domainparse
  60. boolDnsParse(char*domain,87); font-weight:bold; background-color:inherit">char*ip);
  61. ODSocket&operator=(SOCKETs);
  62. operatorSOCKET();
  63. protected:
  64. SOCKETm_sock;
  65. fd_setfdR;
  66. };
  67. #endif
对于这个类 我主要讲解四个方法 一个就是Connect 这个方法 这个主要是用来连接的

第二个 Send 方法 这个主要是用来发送数据的

第三个方法Recv 这个主要是用来接收数据的、

第四个方法Select 这个主要用来判断当前socket 的状态,这里我只用了 判断是否有数据回来这个方法。


这里说明一下 一旦调用recv 这个方法 他会一直等到读取到数据,所以在我们正常的开发游戏过程中。我们都会把它放到单独的线程中来执行。防止我们的主线程卡死。

好下面介绍一下我们的 连接线程类

copy
    #pragmaonce
  1. #include"ODSocket.h"
  2. #include"pthread.h"
  3. classSocketThread
  4. {
  5. public:
  6. ~SocketThread(void);
  7. staticSocketThread*GetInstance();
  8. intstart();
  9. ODSocketgetSocket();
  10. intstate;//0表示连接成功1表示连接失败
  11. ODSocketcsocket;
  12. voidstop();//函数中止当前线程。
  13. private:
  14. pthread_tpid;
  15. staticvoid*start_thread(void*);//静态成员函数,相当于C中的全局函数
  16. SocketThread(staticSocketThread*m_pInstance;
  17. };

这个线程类是用来连接 我们的 服务器的 大家看到我上面的方法就可以才想到 我这个类是一个单利的模式。因为一个游戏中正常情况下只需要一个 套接字即可。所以我这个类采用了单利的模式可以获取一个套接字对象 。

下面贴上实现

copy

    #include"SocketThread.h"
  1. #include"cocos2d.h"
  2. #include"ResPonseThread.h"
  3. USING_NS_CC;
  4. intSocketThread::start(){
  5. interrCode=0;
  6. do{
  7. pthread_attr_ttAttr;
  8. errCode=pthread_attr_init(&tAttr);
  9. CC_BREAK_IF(errCode!=0);
  10. //但是上面这个函数其他内容则主要为你创建的线程设定为分离式
  11. errCode=pthread_attr_setdetachstate(&tAttr,PTHREAD_CREATE_DETACHED);
  12. if(errCode!=0){
  13. pthread_attr_destroy(&tAttr);
  14. break;
  15. }
  16. errCode=pthread_create(&pid,&tAttr,start_thread,this);
  17. }while(0);
  18. returnerrCode;
  19. void*SocketThread::start_thread(void*arg){
  20. SocketThread*thred=(SocketThread*)arg;
  21. ODSocketcdSocket;
  22. cdSocket.Init();
  23. boolisok=cdSocket.Create(AF_INET,SOCK_STREAM,0);
  24. booliscon=cdSocket.Connect("127.0.0.1",8888);
  25. if(iscon){
  26. thred->state=0;
  27. ResPonseThread::GetInstance()->start();//启动响应参数
  28. CCLOG("conection");
  29. }else{
  30. thred->state=1;
  31. }
  32. thred->csocket=cdSocket;
  33. returnNULL;
  34. ODSocketSocketThread::getSocket(){
  35. returnthis->csocket;
  36. SocketThread*SocketThread::m_pInstance=newSocketThread;
  37. SocketThread*SocketThread::GetInstance(){
  38. returnm_pInstance;
  39. voidSocketThread::stop(){
  40. pthread_cancel(pid);
  41. pthread_detach(pid);
  42. SocketThread::SocketThread(void)
  43. {
  44. SocketThread::~SocketThread(if(m_pInstance!=NULL){
  45. deletem_pInstance;
  46. }
对于多线程不是很熟悉的同学们可以在 百度 下相关的资料。只要实现了上面的类,我们就可以连接tong服务器了

下面贴出

接收线程类

copy

    //此类主要处理服务器推送过来的消息
  1. #include"BaseResponseMsg.h"
  2. typedefvoid(cocos2d::CCObject::*ResPonseThreadEvent)(BaseResponseMsg*);
  3. #definecallFunc_selectormsg(_SELECTOR)(ResPonseThreadEvent)(&_SELECTOR)
  4. #defineM_ADDCALLBACKEVENT(varName)
  5. protected:cocos2d::CCObject*m_##varName##listener;ResPonseThreadEventvarName##selector;
  6. public:voidadd##varName##ListenerEvent(ResPonseThreadEventm_event,cocos2d::CCObject*listener){m_##varName##listener=listener;varName##selector=m_event;}
  7. classResPonseThread
  8. ~ResPonseThread(void);
  9. staticResPonseThread*GetInstance();//获取该类的单利
  10. intstart(void*=NULL);//函数是线程启动函数,其输入参数是无类型指针。
  11. voidsleep(inttesec);//函数让当前线程休眠给定时间,单位为毫秒秒。
  12. voiddetach();//
  13. void*wait();
  14. ResPonseThread( pthread_thandle;
  15. boolstarted;
  16. booldetached;
  17. void*threadFunc(void*);
  18. staticResPonseThread*m_pInstance;
  19. M_ADDCALLBACKEVENT(msg);//聊天回调函数
  20. M_ADDCALLBACKEVENT(notcon);//断网回调函数
  21. 这个并不算一个完整的类,因为不同的命令对应的回调函数不一样 当然你也可以弄成一个回调函数,不过我喜欢把东西分开来写

    实现代码

    copy

      #include"BaseResponseMsg.h"
    1. ResPonseThread*ResPonseThread::m_pInstance=newResPonseThread;
    2. ResPonseThread*ResPonseThread::GetInstance(){
    3. ResPonseThread::ResPonseThread(this->m_msglistener=NULL;
    4. started=detached=false;
    5. ResPonseThread::~ResPonseThread(void)
    6. stop();
    7. intResPonseThread::start(void*param){
    8. interrCode=0;
    9. do{
    10. pthread_attr_tattributes;
    11. errCode=pthread_attr_init(&attributes);
    12. CC_BREAK_IF(errCode!=0);
    13. //但是上面这个函数其他内容则主要为你创建的线程设定为分离式
    14. errCode=pthread_attr_setdetachstate(&attributes,PTHREAD_CREATE_DETACHED);
    15. if(errCode!=0){
    16. pthread_attr_destroy(&attributes);
    17. break;
    18. errCode=pthread_create(&handle,&attributes,threadFunc,153); font-weight:bold; background-color:inherit">this);
    19. started=true;
    20. void*ResPonseThread::threadFunc(void*arg){
    21. ResPonseThread*thred=(ResPonseThread*)arg;
    22. ODSocketcsocket=SocketThread::GetInstance()->getSocket();
    23. if(SocketThread::GetInstance()->state==0){
    24. while(true){
    25. //表示服务器端有消息推送过来
    26. if(csocket.Select()==-2){
    27. charrecvBuf[8];//获取请求头的数据
    28. inti=csocket.Recv(recvBuf,8,0);
    29. if(i==8){
    30. chardc1[2]={recvBuf[1],recvBuf[0]};
    31. shortlen=*(short*)&dc1[0];
    32. chardc2[2]={recvBuf[3],recvBuf[2]};
    33. shortcode=*(short*)&dc2[0];
    34. chardc3[4]={recvBuf[7],recvBuf[6],recvBuf[5],recvBuf[4]};
    35. intplayId=*(int*)&dc3[0];
    36. CCLOG("%d",playId);
    37. char*messbody=NULL;
    38. intmyl=0;
    39. if(len>8){
    40. myl=len-8;
    41. messbody=newchar[myl];
    42. csocket.Recv(messbody,myl,0);
    43. ////1001=com.lx.command.player.LoginCmd
    44. //1002=com.lx.command.player.RegisterCmd
    45. //1003=com.lx.command.player.HeartBeatCmd
    46. //登陆
    47. BaseResponseMsg*basmsg=newBaseResponseMsg();
    48. basmsg->code=code;
    49. basmsg->len=len;
    50. basmsg->playerId=playId;
    51. //表示服务器推动过来的消息
    52. if(code==1000){
    53. if(thred->m_msglistener){
    54. basmsg->setStringToMsg(messbody,myl);
    55. (thred->m_msglistener->*(thred->msgselector))(basmsg);
    56. else{
    57. CCLOG("%d",code);
    58. if(thred->m_notconlistener){
    59. basmsg->state=1;//连接断开
    60. (thred->m_notconlistener->*(thred->notconselector))(basmsg);
    61. voidResPonseThread::stop(){
    62. if(started&&!detached){
    63. pthread_cancel(handle);
    64. pthread_detach(handle);
    65. detached=void*ResPonseThread::wait(){
    66. void*status=NULL;
    67. if(started&&!detached){
    68. pthread_join(handle,&status);
    69. returnstatus;
    70. voidResPonseThread::sleep(intsecstr){
    71. timevaltimeout={secstr/1000,secstr%1000};
    72. select(0,NULL,&timeout);
    73. voidResPonseThread::detach(){
    74. pthread_detach(handle);
    75. }

    当大家看到接收代码的时候或许很困惑。这里是引文大小端的问。还有前八个字节是我们规定好的,所以只要解析出前八个字节我们就知道服务器给传送过来的什么。 然后调用响应的回调 通知请求方即可。

    下面贴出 一个消息体组装类

    copy

      #include<string.h>
    1. #include"ConvertEndianUtil.h"
    2. structmessageHead{
    3. shortlen;
    4. shortcode;
    5. intplayerid;
    6. }messagehead;
    7. template<typenameRquest>classBaseRequestMsg
    8. BaseRequestMsg( ~BaseRequestMsg(voidsetRequestMessage(Rquestmessage);//设置请求体
    9. voidsetMessageHead(shortcode,87); font-weight:bold; background-color:inherit">intplayer=0);//设置请求头
    10. boolsendMessage();//发送信息
    11. private:
    12. Rquestrequestmessage;
    13. messageheadmessageHead;
    14. char*getSendMessage();
    15. shortdateLength;
    16. std::stringrequestMessage;
    17. };
    18. typenameRquest>
    19. BaseRequestMsg<Rquest>::BaseRequestMsg(void){
    20. typenameRquest>
    21. BaseRequestMsg<Rquest>::~BaseRequestMsg(void){
    22. voidBaseRequestMsg<Rquest>::setRequestMessage(Rquestmessage){
    23. std::stringdata;
    24. message.SerializeToString(&data);
    25. this->requestMessage=data;
    26. voidBaseRequestMsg<Rquest>::setMessageHead(intplayer){
    27. messageHead.code=ConvertEndianUtil::convertEndianShort(code);
    28. messageHead.playerid=ConvertEndianUtil::convertForInt(player);
    29. char*BaseRequestMsg<Rquest>::getSendMessage(){
    30. shorttotal=8+requestMessage.length();
    31. dateLength=total;
    32. messageHead.len=ConvertEndianUtil::convertEndianShort(total);
    33. char*requestmessage=char[total];
    34. char*requestmessagehead=(char*)&messageHead;
    35. inti=sizeof(messageHead);
    36. intlen=sizeof(requestMessage.c_str())/sizeof(char);
    37. memcpy(requestmessage,requestmessagehead,8);
    38. memcpy(&requestmessage[8],requestMessage.c_str(),requestMessage.length());
    39. returnrequestmessage;
    40. boolBaseRequestMsg<Rquest>::sendMessage(){
    41. ODSocketcSocket=SocketThread::GetInstance()->getSocket();
    42. char*dd=this->getSendMessage();
    43. intcout=cSocket.Send(this->getSendMessage(),this->dateLength,153); font-weight:bold; background-color:inherit">if(cout==this->dateLength){
    44. false;
    45. }
    这里采用了C++ 类模板 这样可以让我们的程序更通用一点。 这个主要是针对protobuf 协议体的组装。

    下面给大家贴上一段调用代码

    copy

      BaseRequestMsg<zzboy::protobuf::ChatMsgReq>*baserlong=newBaseRequestMsg<zzboy::protobuf::ChatMsgReq>();
    1. zzboy::protobuf::ChatMsgReqreq;
    2. req.set_msgtype(1);
    3. req.set_message("ddddd");
    4. baserlong->setMessageHead((short)1000,(int)1);
    5. baserlong->setRequestMessage(req);
    6. baserlong->sendMessage();
    这就是一个发送消息的方法,哈哈看起来很简单吧。

    这里我做的demo 是 聊天系统。在很多游戏里面都有聊天。这里就是一个简单的实现。不过整体的思路应该是这样

    红色区域内 1 表示我自己说的话 4545 是别人说的话。其实只要服务器通知我 有人给我发送消息。都会在这里展示出来。

    在这里我遇见一个BUG 就是 CCLabelTTF* lable = CCLabelTTF::create(tem,"Arial",24); 这个标签的创建 在线程的回调函数中我始终穿件不成功。导致我用了另外的一个办法解决。这里如果谁知道这个BUG 为什么 请私信给我活着留言给我。咱们共同进步

    哈哈 写到这里网络连接着一块就完了,其实感觉也没什么,最重要的就是你解析过数据之后要干什么。大家发现BOY 是不是全才 服务器和客户端都会 。我感觉如果时间允许我自己可以做一个小型的多人在线网络游戏。 哈哈。

    关于上面讲到的可能有些人还是不明白。可以留言给我或者在码农哥的群里给我说,如果我看到了都会给大家讲解。这两天这是忍着病给大家写的 有哪些写的不好请见谅。

    客户端源码下载

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读