Cocos2d-x Tutorial 之 Socket的使用(1)

来源:互联网 发布:淘宝情趣用品买家秀 编辑:程序博客网 时间:2024/06/06 09:25

本篇博文记录下 cocos2-x 中 socket 链接的建立

使用到了第三方跨平台的 socket 库 netlink

主页

下载地址

引擎版本:quick-cocos2d-x 3.3

一、配置环境

  • 1、下载并增加到 Xcode

从netlink官网下载netlink的包,解压缩并将解压缩后的文件夹放到 Classes 目录下,在 Xcode 中右键 Add Files ,增加 netLink 到项目中,打开 netLink group 删除多余的条目,只留下 include 和 src 即可。

  • 2、配置 SearchPath

打开项目文件,选择 project (如果只需要给某一个 target 增加,选择相应的 target ),选择 Build Settings ,选择 Search Paths ,给 User Header Search Path 增加

    $(SRCROOT)/../Classes/netLink/include
  • 3、编译项目 注意这时候编译的时候可能会报一个错误,如下图

解决办法:

打开 netLink/include/netlink/core.h, 在末尾加上 #include “unistd.h”

1234
#include "netlink/exception.h"#include "netlink/release_manager.h"#include "netlink/util.h"#include "unistd.h"

然后在编译,以上编译成功说明环境配置ok

二、编写 Socket 工具类

Socket工具类就直接贴代码加注释了

SocketUtil.h

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
#ifndef __MyLuaGame__SocketUtil__#define __MyLuaGame__SocketUtil__#include <iostream>#include "cocos2d.h"namespace NL {    class SocketGroup;};class ReceiveDeal;class DisconnectDeal;class SocketUtil : public cocos2d::Ref{public:    typedef std::function<void(std::vector<int>)> ccMsgFunc;    typedef std::function<void(bool)> ccStateFunc;    //单例    static SocketUtil* getInstance();    //建立socket    bool connectServer(const std::string & strIp, unsigned int nPort);    //发送消息    bool sendMsg(std::vector<int> & bytes);    //接收消息    void receiveMsg(std::vector<int> & bytes);    //socket状态改变事件    void socketStateChange(bool connected);    //关闭链接    void close();    //增加消息回调    void addMsgHandler(const ccMsgFunc& func);    //增加状态回调    void addStateHandler(const ccStateFunc& func);    //增加消息回调 Lua    void addMsgHandlerLua(const int func);    //增加状态回调 Lua    void addStateHandlerLua(const int func);private:    SocketUtil();    virtual ~SocketUtil();    //socket线程    void connect_thread();    //抛出消息    void dispatchMsg(float dt);public:    std::string _ip;//ip地址    unsigned int _port;//端口    bool _disconnect;//socket链接状态    std::mutex _mutex;//锁    NL::SocketGroup * _socketGroup;//socket组    ReceiveDeal * _receiveDeal;//消息处理类对象    DisconnectDeal * _disconnectDeal;//断线处理类对象    std::vector<std::vector<int>> _result;//消息集合    std::vector<bool> _socketResult;//网络状态消息集合    ccMsgFunc _msgHandler;    ccStateFunc _stateHandler;    int _msgHandlerLua;    int _stateHandlerLua;private:    static SocketUtil* _instance;};#endif /* defined(__MyLuaGame__SocketUtil__) */

SocketUtil.cpp

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
#include "SocketUtil.h"#include "netlink/netlink.h"#include "CCLuaEngine.h"#include "LuaBasicConversions.h"using namespace std;using namespace cocos2d;using namespace NL;#pragma mark - ReceiveDealclass ReceiveDeal : public NL::SocketGroupCmd {public:    virtual void exec(NL::Socket* socket, NL::SocketGroup* group, void* reference)    {        int readSize=socket->nextReadSize();//读取当前可读取的数据长度        unsigned char readCh[readSize];//定义存储数据的字节数组        socket->read(readCh, readSize);//读取        vector<int> v;                 //此处是针对我们项目,可以删掉直接传        for (int i=0; i < readSize; i++) {            v.push_back(readCh[i]);        }        SocketUtil::getInstance()->receiveMsg(v);//发送服务端消息    }};#pragma mark - DisconnectDealclass DisconnectDeal : public NL::SocketGroupCmd{public:    virtual void exec(NL::Socket* socket, NL::SocketGroup* group, void* reference)    {        SocketUtil::getInstance()->close();         //关闭链接        SocketUtil::getInstance()->socketStateChange(false);//发送断线消息    }};#pragma mark - SocketUtilSocketUtil* SocketUtil::_instance = nullptr;SocketUtil* SocketUtil::getInstance(){    if(_instance == nullptr)    {        _instance = new SocketUtil();    }    return _instance;}SocketUtil::SocketUtil(){    _ip = "127.0.0.1";    _port = 3000;    _disconnect = true;    _msgHandler = nullptr;    _stateHandler = nullptr;    _msgHandlerLua = 0;    _stateHandlerLua = 0;    std::lock_guard<std::mutex> lg(_mutex);    _socketGroup = new SocketGroup();    _receiveDeal = new ReceiveDeal();    _disconnectDeal = new DisconnectDeal();    Scheduler* scheduler = Director::getInstance()->getScheduler();//启动一个定时器去轮循分发消息    scheduler->schedule(schedule_selector(SocketUtil::dispatchMsg), this, 0, false);}SocketUtil::~SocketUtil(){    delete _socketGroup;    delete _receiveDeal;    delete _disconnectDeal;    Scheduler* scheduler = Director::getInstance()->getScheduler();//停止定时器    scheduler->unschedule(schedule_selector(SocketUtil::dispatchMsg),this);}bool SocketUtil::connectServer(const string & strIp, unsigned int nPort) {    if (!_disconnect) {        printf("socket is already connected\n");        return true;    }    this->_ip= strIp;    this->_port = nPort;    try {        NL::init();//初始化        _socketGroup->setCmdOnRead(_receiveDeal);//设置消息处理        _socketGroup->setCmdOnDisconnect(_disconnectDeal);//设置断线处理        thread connectThread(&SocketUtil::connect_thread, this);//连接操作是阻塞的,因此启动一个子线程去连接        connectThread.detach();    } catch (NL::Exception e) {        cout << "connectServer: \n***ERROR*** " << e.what();    }    return false;}void SocketUtil::connect_thread(){    try {        Socket* socket = new Socket(_ip, _port);        _socketGroup->add(socket);        //socket建立成功        _disconnect = false;        socketStateChange(true);        while (true) {            if (_disconnect) {                Socket* socket = _socketGroup->get(0);                socket->disconnect();                _socketGroup->remove(socket);                delete socket;                break;            }            try {                _socketGroup->listen(500);            } catch (NL::Exception e) {                cout << "listen_thread: \n***ERROR*** " << e.what();            }        }    } catch (NL::Exception e) {        cout << "connect_thread: \n***ERROR*** " << e.what();        //socket建立失败        _disconnect = true;        socketStateChange(false);    }}bool SocketUtil::sendMsg(std::vector<int> & bytes)//此处也可以改造{    if (_disconnect) {        printf("socket disconnect\n");        return false;    }    unsigned long int bytesLen = bytes.size();    //转换成 byte 数组    unsigned char ch[bytesLen];    for (int i=0; i<bytesLen; i++) {        ch[i] = bytes.at(i);    }    try {        //发送        _socketGroup->get(0)->send(ch,bytesLen);        return true;    } catch (NL::Exception e) {        cout << "sendMsg: \n***ERROR*** " << e.what() << endl;        return false;    }}void SocketUtil::receiveMsg(std::vector<int> & bytes){    _mutex.lock();//加锁    _result.push_back(bytes);    _mutex.unlock();//解锁}void SocketUtil::socketStateChange(bool connected){    _mutex.lock();//加锁    _socketResult.push_back(connected);    _mutex.unlock();//解锁}void SocketUtil::dispatchMsg(float dt){    _mutex.lock();//加锁    for (int i=0; i<_socketResult.size(); i++) {        bool connected = _socketResult.at(i);        //通知游戏网络状态        if (_stateHandler != nullptr) {            _stateHandler(connected);        }        if (_stateHandlerLua != 0) {            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();            stack->pushBoolean(connected);            stack->executeFunctionByHandler(_stateHandlerLua, 1);        }    }    _socketResult.clear();    for (int i=0; i<_result.size(); i++) {        vector<int> bytes = _result.at(i);        //通知游戏收到数据        if (_msgHandler != nullptr) {            _msgHandler(bytes);        }        if (_msgHandlerLua != 0) {            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();            ccvector_int_to_luaval(stack->getLuaState(), bytes);            stack->executeFunctionByHandler(_msgHandlerLua, 1);        }    }    _result.clear();    _mutex.unlock();//解锁}void SocketUtil::close(){    //释放socket占用的资源    _disconnect = true;    socketStateChange(false);}void SocketUtil::addMsgHandler(const ccMsgFunc &func){    _msgHandler = func;}void SocketUtil::addStateHandler(const ccStateFunc &func){    _stateHandler = func;}void SocketUtil::addMsgHandlerLua(const int func){    _msgHandlerLua = func;}void SocketUtil::addStateHandlerLua(const int func){    _stateHandlerLua = func;}

编译测试是否通过,通过即可进行下一步

三、编写服务端代码

新建一个 test.js 文件,并拷贝如下代码,监听 127.0.0.1 的 3000 端口。

下面代码作用是:客户端发送的内容,原封不动的返回回去。

test.js

1234567
var net = require('net');var server = net.createServer(function (socket) {  socket.pipe(socket);});server.listen(3000, '127.0.0.1');

保存代码为 test.js 并在命令行执行 node test.js 即可启动一个简单的 socket 服务端

可以 telnet 一下试试是否启动成功

1
telnet 127.0.0.1 3000

四、客户端链接服务器

修改 AppDelegate.h

继承 Ref,增加函数 schedulerSendMsg

AppDelegate.h

123456789
class  AppDelegate : private cocos2d::Application, public cocos2d::Ref{    ...    void schedulerSendMsg(float dt);    void schedulerClose(float dt);    void msgHandler(std::vector<int> bytes);    void stateHandler(bool state);    ...};

修改 AppDelegate.cpp

启动以后链接 socket,等待2秒,发送1条消息

AppDelegate.cpp

123456789101112131415161718192021222324252627282930313233343536
bool AppDelegate::applicationDidFinishLaunching(){    ...    //增加回调    SocketUtil::getInstance()->addMsgHandler(CC_CALLBACK_1(AppDelegate::msgHandler, this));    SocketUtil::getInstance()->addStateHandler(CC_CALLBACK_1(AppDelegate::stateHandler, this));    //建立链接    SocketUtil::getInstance()->connectServer("127.0.0.1", 3000);    //等待2秒,发送两条数据    Director::getInstance()->getScheduler()->schedule(CC_CALLBACK_1(AppDelegate::schedulerSendMsg, this), this, 0, 1, 2, false, "send msg");    //等待4秒,关闭链接    Director::getInstance()->getScheduler()->schedule(CC_CALLBACK_1(AppDelegate::schedulerClose, this), this, 0, 0, 4, false, "close");    ...}void AppDelegate::schedulerSendMsg(float dt){    string myString = "hello server";    std::vector<int> bytes(myString.begin(), myString.end());    SocketUtil::getInstance()->sendMsg(bytes);}void AppDelegate::schedulerClose(float dt){    SocketUtil::getInstance()->close();}void AppDelegate::msgHandler(std::vector<int> bytes){    printf("receive data\n");}void AppDelegate::stateHandler(bool state){    printf("state change: %s\n", state?"true":"false");}

启动以后,等待几秒,检查控制台输出

1234
state change: truereceive datareceive datastate change: false

至此,Socket前端和后端建立链接,发送和接受消息完成。

–EOF–

原创粉丝点击