Cocos2d-x Tutorial 之 Socket的使用(1)
来源:互联网 发布:淘宝情趣用品买家秀 编辑:程序博客网 时间:2024/06/06 09:25
本篇博文记录下 cocos2-x 中 socket 链接的建立
使用到了第三方跨平台的 socket 库 netlink
主页
下载地址
引擎版本:quick-cocos2d-x 3.3
一、配置环境
从netlink官网下载netlink的包,解压缩并将解压缩后的文件夹放到 Classes 目录下,在 Xcode 中右键 Add Files ,增加 netLink 到项目中,打开 netLink group 删除多余的条目,只留下 include 和 src 即可。
打开项目文件,选择 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 一下试试是否启动成功
四、客户端链接服务器
修改 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–