mirror of https://github.com/axmolengine/axmol.git
Implements a socket.io client extension, adds a test case and updates the android makefiles.
Contains the following tasks: -initial socket.io extension commit -sioclientimpl subclasses websocket:delegate to respond to websocket events -implement static connect methods and basic client creation -move SocketIO class files into correct extension folder (network) -create SocketIO test in TestCpp -update project references -add missing static modifier to connect method -implement basic test methods -update extensions tests with SocketIO test entry -implement basic handshake and opensocket methods for SocketIO -add Delegate class to handle callbacks, implement virtual Delegate methods in test -implement socket and client registries for lookup when connecting to hosts and endpoints -connect delegate onOpen method by separating impl creation from connection -update test to demonstrate onOpen callback -create send and emit methods, move SIOClient into header file, add send and emit methods to test -implement basic socket.io message parsing -improve logging for events and messages -add logic to pull event name from payload -schedule heartbeat to keep connection alive, scheduled for 90% of the heartbeat interval from the server for safety -add onConnect handler to to catch socket.io onconnect vs websocket onopen -add disconnect and disconnectFromEndpoint methods to properly disconnect and destroy objects -modify SIOClientImpl to track _uri for easier lookup in registries -connect handler for onMessage to message event from socket.io, modify onError handler to take a string instead of WebSocket error code -create SIOEvent callback type, implement event registry in clients, add test for event registration and callback -update SIOEvent to use std::function and c++11, utilize cocos2d CC_CALLBACK method to bind selectors, this ensures that the *this reference is properly passed -check for connect before sending or emitting in the client, cleanup some codes -change connect logic to reuse existing socket connections instead of opening a new one -implements get and set Tag methods for clients for easy reference -improve endpoint handling, add endpoint tests to test layer -additional error handling within socket disconnect error and failure to open connection -fixes extracting endpoint from socket.io messages (in cases of the connect message, where there is 1 less colon for the connect message to the default namespace). Also fixes connecting to the default namespace "/" in the connectToEndpoint method -add disconnect and onClose handlers to client so that onClose is called in the delegate -add disconnect test methods to test layers -change c-style casts to static_casts when using a CCDICT_FOREACH -remove some unneeded namespace completion -add usage documentation -add handling for disconnect from server, cleanup some codes -update comments and documentation in the socketiotest -update includes so the NDK doesn't complain when compiling with c++11 -add socketio.cpp and test.cpp to the android makefiles -update test URL to my public server, test script can also be found in my repo at https://github.com/hannon235/socket.io-testserver.git Signed-off-by: Chris Hannon <himynameschris@gmail.com>
This commit is contained in:
parent
cfc12d780c
commit
126ebb888c
|
@ -78,6 +78,7 @@ GUI/CCEditBox/CCEditBox.cpp \
|
|||
GUI/CCEditBox/CCEditBoxImplAndroid.cpp \
|
||||
network/HttpClient.cpp \
|
||||
network/WebSocket.cpp \
|
||||
network/SocketIO.cpp \
|
||||
physics_nodes/CCPhysicsDebugNode.cpp \
|
||||
physics_nodes/CCPhysicsSprite.cpp \
|
||||
LocalStorage/LocalStorageAndroid.cpp \
|
||||
|
|
|
@ -0,0 +1,714 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013 Chris Hannon
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*based on the SocketIO library created by LearnBoost at http://socket.io
|
||||
*using spec version 1 found at https://github.com/LearnBoost/socket.io-spec
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "SocketIO.h"
|
||||
#include "cocos-ext.h"
|
||||
#include "network/WebSocket.h"
|
||||
#include <algorithm>
|
||||
|
||||
NS_CC_EXT_BEGIN
|
||||
|
||||
//class declarations
|
||||
|
||||
/**
|
||||
* @brief The implementation of the socket.io connection
|
||||
* Clients/endpoints may share the same impl to accomplish multiplexing on the same websocket
|
||||
*/
|
||||
class SIOClientImpl :
|
||||
public Object,
|
||||
public WebSocket::Delegate
|
||||
{
|
||||
private:
|
||||
int _port, _heartbeat, _timeout;
|
||||
std::string _host, _sid, _uri;
|
||||
bool _connected;
|
||||
|
||||
WebSocket *_ws;
|
||||
|
||||
Dictionary* _clients;
|
||||
|
||||
public:
|
||||
SIOClientImpl(const std::string& host, int port);
|
||||
virtual ~SIOClientImpl(void);
|
||||
|
||||
static SIOClientImpl* create(const std::string& host, int port);
|
||||
|
||||
virtual void onOpen(cocos2d::extension::WebSocket* ws);
|
||||
virtual void onMessage(cocos2d::extension::WebSocket* ws, const cocos2d::extension::WebSocket::Data& data);
|
||||
virtual void onClose(cocos2d::extension::WebSocket* ws);
|
||||
virtual void onError(cocos2d::extension::WebSocket* ws, const cocos2d::extension::WebSocket::ErrorCode& error);
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
bool init();
|
||||
void handshake();
|
||||
void handshakeResponse(HttpClient *sender, HttpResponse *response);
|
||||
void openSocket();
|
||||
void heartbeat(float dt);
|
||||
|
||||
SIOClient* getClient(const std::string& endpoint);
|
||||
void addClient(const std::string& endpoint, SIOClient* client);
|
||||
|
||||
void connectToEndpoint(const std::string& endpoint);
|
||||
void disconnectFromEndpoint(const std::string& endpoint);
|
||||
|
||||
void send(std::string endpoint, std::string s);
|
||||
void emit(std::string endpoint, std::string eventname, std::string args);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
//method implementations
|
||||
|
||||
//begin SIOClientImpl methods
|
||||
SIOClientImpl::SIOClientImpl(const std::string& host, int port) :
|
||||
_port(port),
|
||||
_host(host),
|
||||
_connected(false)
|
||||
{
|
||||
_clients = Dictionary::create();
|
||||
_clients->retain();
|
||||
|
||||
std::stringstream s;
|
||||
s << host << ":" << port;
|
||||
_uri = s.str();
|
||||
|
||||
_ws = NULL;
|
||||
|
||||
}
|
||||
|
||||
SIOClientImpl::~SIOClientImpl() {
|
||||
|
||||
if(_connected) disconnect();
|
||||
|
||||
CC_SAFE_DELETE(_clients);
|
||||
CC_SAFE_DELETE(_ws);
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::handshake() {
|
||||
CCLog("SIOClientImpl::handshake() called");
|
||||
|
||||
std::stringstream pre;
|
||||
pre << "http://" << _uri << "/socket.io/1";
|
||||
|
||||
HttpRequest* request = new HttpRequest();
|
||||
request->setUrl(pre.str().c_str());
|
||||
request->setRequestType(HttpRequest::kHttpGet);
|
||||
|
||||
request->setResponseCallback(this, httpresponse_selector(SIOClientImpl::handshakeResponse));
|
||||
request->setTag("handshake");
|
||||
|
||||
CCLog("SIOClientImpl::handshake() waiting");
|
||||
|
||||
HttpClient::getInstance()->send(request);
|
||||
|
||||
request->release();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void SIOClientImpl::handshakeResponse(HttpClient *sender, HttpResponse *response) {
|
||||
|
||||
CCLog("SIOClientImpl::handshakeResponse() called");
|
||||
|
||||
if (0 != strlen(response->getHttpRequest()->getTag()))
|
||||
{
|
||||
CCLog("%s completed", response->getHttpRequest()->getTag());
|
||||
}
|
||||
|
||||
int statusCode = response->getResponseCode();
|
||||
char statusString[64] = {};
|
||||
sprintf(statusString, "HTTP Status Code: %d, tag = %s", statusCode, response->getHttpRequest()->getTag());
|
||||
CCLog("response code: %d", statusCode);
|
||||
|
||||
if (!response->isSucceed())
|
||||
{
|
||||
CCLog("SIOClientImpl::handshake() failed");
|
||||
CCLog("error buffer: %s", response->getErrorBuffer());
|
||||
|
||||
DictElement* el = NULL;
|
||||
|
||||
CCDICT_FOREACH(_clients, el) {
|
||||
|
||||
SIOClient* c = static_cast<SIOClient*>(el->getObject());
|
||||
|
||||
c->getDelegate()->onError(c, response->getErrorBuffer());
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CCLog("SIOClientImpl::handshake() succeeded");
|
||||
|
||||
std::vector<char> *buffer = response->getResponseData();
|
||||
std::stringstream s;
|
||||
|
||||
for (unsigned int i = 0; i < buffer->size(); i++)
|
||||
{
|
||||
s << (*buffer)[i];
|
||||
}
|
||||
|
||||
CCLog("SIOClientImpl::handshake() dump data: %s", s.str().c_str());
|
||||
|
||||
std::string res = s.str();
|
||||
std::string sid;
|
||||
int pos;
|
||||
int heartbeat, timeout;
|
||||
|
||||
pos = res.find(":");
|
||||
if(pos >= 0) {
|
||||
sid = res.substr(0, pos);
|
||||
res.erase(0, pos+1);
|
||||
}
|
||||
|
||||
pos = res.find(":");
|
||||
if(pos >= 0){
|
||||
heartbeat = atoi(res.substr(pos+1, res.size()).c_str());
|
||||
}
|
||||
|
||||
pos = res.find(":");
|
||||
if(pos >= 0){
|
||||
timeout = atoi(res.substr(pos+1, res.size()).c_str());
|
||||
}
|
||||
|
||||
_sid = sid;
|
||||
_heartbeat = heartbeat;
|
||||
_timeout = timeout;
|
||||
|
||||
openSocket();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::openSocket() {
|
||||
|
||||
CCLog("SIOClientImpl::openSocket() called");
|
||||
|
||||
std::stringstream s;
|
||||
s << _uri << "/socket.io/1/websocket/" << _sid;
|
||||
|
||||
_ws = new WebSocket();
|
||||
if(!_ws->init(*this, s.str()))
|
||||
{
|
||||
CC_SAFE_DELETE(_ws);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool SIOClientImpl::init() {
|
||||
|
||||
CCLog("SIOClientImpl::init() successful");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::connect() {
|
||||
|
||||
this->handshake();
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::disconnect() {
|
||||
|
||||
if(_ws->getReadyState() == WebSocket::kStateOpen) {
|
||||
|
||||
std::string s = "0::";
|
||||
|
||||
_ws->send(s);
|
||||
|
||||
CCLog("Disconnect sent");
|
||||
|
||||
_ws->close();
|
||||
|
||||
}
|
||||
|
||||
Director::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);
|
||||
|
||||
_connected = false;
|
||||
|
||||
SocketIO::instance()->removeSocket(_uri);
|
||||
|
||||
}
|
||||
|
||||
SIOClientImpl* SIOClientImpl::create(const std::string& host, int port) {
|
||||
|
||||
SIOClientImpl *s = new SIOClientImpl(host, port);
|
||||
|
||||
if(s && s->init()) {
|
||||
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
SIOClient* SIOClientImpl::getClient(const std::string& endpoint) {
|
||||
|
||||
return static_cast<SIOClient*>(_clients->objectForKey(endpoint));
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::addClient(const std::string& endpoint, SIOClient* client) {
|
||||
|
||||
_clients->setObject(client, endpoint);
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::connectToEndpoint(const std::string& endpoint) {
|
||||
|
||||
std::string path = endpoint == "/" ? "" : endpoint;
|
||||
|
||||
std::string s = "1::" + path;
|
||||
|
||||
_ws->send(s);
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::disconnectFromEndpoint(const std::string& endpoint) {
|
||||
|
||||
_clients->removeObjectForKey(endpoint);
|
||||
|
||||
if(_clients->count() == 0 || endpoint == "/") {
|
||||
|
||||
CCLog("SIOClientImpl::disconnectFromEndpoint out of endpoints, checking for disconnect");
|
||||
|
||||
if(_connected) this->disconnect();
|
||||
|
||||
} else {
|
||||
|
||||
std::string path = endpoint == "/" ? "" : endpoint;
|
||||
|
||||
std::string s = "0::" + path;
|
||||
|
||||
_ws->send(s);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::heartbeat(float dt) {
|
||||
|
||||
std::string s = "2::";
|
||||
|
||||
_ws->send(s);
|
||||
|
||||
CCLog("Heartbeat sent");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SIOClientImpl::send(std::string endpoint, std::string s) {
|
||||
std::stringstream pre;
|
||||
|
||||
std::string path = endpoint == "/" ? "" : endpoint;
|
||||
|
||||
pre << "3::" << path << ":" << s;
|
||||
|
||||
std::string msg = pre.str();
|
||||
|
||||
CCLog("sending message: %s", msg.c_str());
|
||||
|
||||
_ws->send(msg);
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::emit(std::string endpoint, std::string eventname, std::string args) {
|
||||
|
||||
std::stringstream pre;
|
||||
|
||||
std::string path = endpoint == "/" ? "" : endpoint;
|
||||
|
||||
pre << "5::" << path << ":{\"name\":\"" << eventname << "\",\"args\":" << args << "}";
|
||||
|
||||
std::string msg = pre.str();
|
||||
|
||||
CCLog("emitting event with data: %s", msg.c_str());
|
||||
|
||||
_ws->send(msg);
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::onOpen(cocos2d::extension::WebSocket* ws) {
|
||||
|
||||
_connected = true;
|
||||
|
||||
SocketIO::instance()->addSocket(_uri, this);
|
||||
|
||||
DictElement* e = NULL;
|
||||
|
||||
CCDICT_FOREACH(_clients, e) {
|
||||
|
||||
SIOClient *c = static_cast<SIOClient*>(e->getObject());
|
||||
|
||||
c->onOpen();
|
||||
|
||||
}
|
||||
|
||||
Director::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(SIOClientImpl::heartbeat), this, (_heartbeat * .9), false);
|
||||
|
||||
CCLog("SIOClientImpl::onOpen socket connected!");
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::onMessage(cocos2d::extension::WebSocket* ws, const cocos2d::extension::WebSocket::Data& data) {
|
||||
|
||||
CCLog("SIOClientImpl::onMessage received: %s", data.bytes);
|
||||
|
||||
int control = atoi(&data.bytes[0]);
|
||||
|
||||
std::string payload, msgid, endpoint, s_data, eventname;
|
||||
payload = data.bytes;
|
||||
|
||||
int pos, pos2;
|
||||
|
||||
pos = payload.find(":");
|
||||
if(pos >=0 ) {
|
||||
payload.erase(0, pos+1);
|
||||
}
|
||||
|
||||
pos = payload.find(":");
|
||||
if(pos > 0 ) {
|
||||
msgid = atoi(payload.substr(0, pos+1).c_str());
|
||||
}
|
||||
payload.erase(0, pos+1);
|
||||
|
||||
pos = payload.find(":");
|
||||
if(pos >= 0) {
|
||||
|
||||
endpoint = payload.substr(0, pos);
|
||||
payload.erase(0, pos+1);
|
||||
|
||||
} else {
|
||||
|
||||
endpoint = payload;
|
||||
}
|
||||
|
||||
if(endpoint == "") endpoint = "/";
|
||||
|
||||
|
||||
s_data = payload;
|
||||
SIOClient *c = NULL;
|
||||
c = getClient(endpoint);
|
||||
if(c == NULL) CCLog("SIOClientImpl::onMessage client lookup returned NULL");
|
||||
|
||||
switch(control) {
|
||||
case 0:
|
||||
CCLog("Received Disconnect Signal for Endpoint: %s\n", endpoint.c_str());
|
||||
if(c) c->receivedDisconnect();
|
||||
disconnectFromEndpoint(endpoint);
|
||||
break;
|
||||
case 1:
|
||||
CCLog("Connected to endpoint: %s \n",endpoint.c_str());
|
||||
if(c) c->onConnect();
|
||||
break;
|
||||
case 2:
|
||||
CCLog("Heartbeat received\n");
|
||||
break;
|
||||
case 3:
|
||||
CCLog("Message received: %s \n", s_data.c_str());
|
||||
if(c) c->getDelegate()->onMessage(c, s_data);
|
||||
break;
|
||||
case 4:
|
||||
CCLog("JSON Message Received: %s \n", s_data.c_str());
|
||||
if(c) c->getDelegate()->onMessage(c, s_data);
|
||||
break;
|
||||
case 5:
|
||||
CCLog("Event Received with data: %s \n", s_data.c_str());
|
||||
|
||||
if(c) {
|
||||
eventname = "";
|
||||
pos = s_data.find(":");
|
||||
pos2 = s_data.find(",");
|
||||
if(pos2 > pos) {
|
||||
s_data = s_data.substr(pos+1, pos2-pos-1);
|
||||
std::remove_copy(s_data.begin(), s_data.end(),
|
||||
std::back_inserter(eventname), '"');
|
||||
}
|
||||
|
||||
c->fireEvent(eventname, payload);
|
||||
}
|
||||
|
||||
break;
|
||||
case 6:
|
||||
CCLog("Message Ack\n");
|
||||
break;
|
||||
case 7:
|
||||
CCLog("Error\n");
|
||||
if(c) c->getDelegate()->onError(c, s_data);
|
||||
break;
|
||||
case 8:
|
||||
CCLog("Noop\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void SIOClientImpl::onClose(cocos2d::extension::WebSocket* ws) {
|
||||
|
||||
if(_clients->count() > 0) {
|
||||
|
||||
DictElement *e;
|
||||
|
||||
CCDICT_FOREACH(_clients, e) {
|
||||
|
||||
SIOClient *c = static_cast<SIOClient *>(e->getObject());
|
||||
|
||||
c->receivedDisconnect();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this->release();
|
||||
|
||||
}
|
||||
|
||||
void SIOClientImpl::onError(cocos2d::extension::WebSocket* ws, const cocos2d::extension::WebSocket::ErrorCode& error) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
//begin SIOClient methods
|
||||
SIOClient::SIOClient(const std::string& host, int port, const std::string& path, SIOClientImpl* impl, SocketIO::SIODelegate& delegate)
|
||||
: _host(host)
|
||||
, _port(port)
|
||||
, _path(path)
|
||||
, _socket(impl)
|
||||
, _connected(false)
|
||||
, _delegate(&delegate)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
SIOClient::~SIOClient(void) {
|
||||
|
||||
if(_connected) {
|
||||
_socket->disconnectFromEndpoint(_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::onOpen() {
|
||||
|
||||
if(_path != "/") {
|
||||
|
||||
_socket->connectToEndpoint(_path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::onConnect() {
|
||||
|
||||
_connected = true;
|
||||
_delegate->onConnect(this);
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::send(std::string s) {
|
||||
|
||||
if(_connected) {
|
||||
_socket->send(_path, s);
|
||||
} else {
|
||||
_delegate->onError(this, "Client not yet connected");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::emit(std::string eventname, std::string args) {
|
||||
|
||||
if(_connected) {
|
||||
_socket->emit(_path, eventname, args);
|
||||
} else {
|
||||
_delegate->onError(this, "Client not yet connected");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::disconnect() {
|
||||
|
||||
_connected = false;
|
||||
|
||||
_socket->disconnectFromEndpoint(_path);
|
||||
|
||||
_delegate->onClose(this);
|
||||
|
||||
this->release();
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::receivedDisconnect() {
|
||||
|
||||
_connected = false;
|
||||
|
||||
_delegate->onClose(this);
|
||||
|
||||
this->release();
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::on(const std::string& eventName, SIOEvent e) {
|
||||
|
||||
_eventRegistry[eventName] = e;
|
||||
|
||||
}
|
||||
|
||||
void SIOClient::fireEvent(const std::string& eventName, const std::string& data) {
|
||||
|
||||
CCLog("SIOClient::fireEvent called with event name: %s and data: %s", eventName.c_str(), data.c_str());
|
||||
|
||||
if(_eventRegistry[eventName]) {
|
||||
|
||||
SIOEvent e = _eventRegistry[eventName];
|
||||
|
||||
e(this, data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CCLog("SIOClient::fireEvent no event with name %s found", eventName.c_str());
|
||||
|
||||
}
|
||||
|
||||
//begin SocketIO methods
|
||||
SocketIO *SocketIO::_inst = NULL;
|
||||
|
||||
SocketIO::SocketIO() {
|
||||
|
||||
_sockets = Dictionary::create();
|
||||
_sockets->retain();
|
||||
|
||||
}
|
||||
|
||||
SocketIO::~SocketIO(void) {
|
||||
CC_SAFE_DELETE(_sockets);
|
||||
delete _inst;
|
||||
}
|
||||
|
||||
SocketIO* SocketIO::instance() {
|
||||
|
||||
if(!_inst)
|
||||
_inst = new SocketIO();
|
||||
|
||||
return _inst;
|
||||
|
||||
}
|
||||
|
||||
SIOClient* SocketIO::connect(SocketIO::SIODelegate& delegate, const std::string& uri) {
|
||||
|
||||
std::string host = uri;
|
||||
int port, pos;
|
||||
|
||||
pos = host.find("//");
|
||||
if(pos >= 0) {
|
||||
host.erase(0, pos+2);
|
||||
}
|
||||
|
||||
pos = host.find(":");
|
||||
if(pos >= 0){
|
||||
port = atoi(host.substr(pos+1, host.size()).c_str());
|
||||
}
|
||||
|
||||
pos = host.find("/", 0);
|
||||
std::string path = "/";
|
||||
if(pos >= 0){
|
||||
path += host.substr(pos + 1, host.size());
|
||||
}
|
||||
|
||||
pos = host.find(":");
|
||||
if(pos >= 0){
|
||||
host.erase(pos, host.size());
|
||||
}else if((pos = host.find("/"))>=0) {
|
||||
host.erase(pos, host.size());
|
||||
}
|
||||
|
||||
std::stringstream s;
|
||||
s << host << ":" << port;
|
||||
|
||||
SIOClientImpl* socket = NULL;
|
||||
SIOClient *c = NULL;
|
||||
|
||||
socket = SocketIO::instance()->getSocket(s.str());
|
||||
|
||||
if(socket == NULL) {
|
||||
//create a new socket, new client, connect
|
||||
socket = SIOClientImpl::create(host, port);
|
||||
|
||||
c = new SIOClient(host, port, path, socket, delegate);
|
||||
|
||||
socket->addClient(path, c);
|
||||
|
||||
socket->connect();
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
//check if already connected to endpoint, handle
|
||||
c = socket->getClient(path);
|
||||
|
||||
if(c == NULL) {
|
||||
|
||||
c = new SIOClient(host, port, path, socket, delegate);
|
||||
|
||||
socket->addClient(path, c);
|
||||
|
||||
socket->connectToEndpoint(path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
SIOClientImpl* SocketIO::getSocket(const std::string& uri) {
|
||||
|
||||
return static_cast<SIOClientImpl*>(_sockets->objectForKey(uri));
|
||||
|
||||
}
|
||||
|
||||
void SocketIO::addSocket(const std::string& uri, SIOClientImpl* socket) {
|
||||
_sockets->setObject(socket, uri);
|
||||
}
|
||||
|
||||
void SocketIO::removeSocket(const std::string& uri) {
|
||||
_sockets->removeObjectForKey(uri);
|
||||
}
|
||||
|
||||
NS_CC_EXT_END
|
|
@ -0,0 +1,179 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013 Chris Hannon http://www.channon.us
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*based on the SocketIO library created by LearnBoost at http://socket.io
|
||||
*using spec version 1 found at https://github.com/LearnBoost/socket.io-spec
|
||||
|
||||
Usage is described below, a full working example can be found in TestCpp under ExtionsTest/NetworkTest/SocketIOTest
|
||||
|
||||
creating a new connection to a socket.io server running at localhost:3000
|
||||
|
||||
SIOClient *client = SocketIO::connect(*delegate, "ws://localhost:3000");
|
||||
|
||||
the connection process will begin and if successful delegate::onOpen will be called
|
||||
if the connection process results in an error, delegate::onError will be called with the err msg
|
||||
|
||||
sending a message to the server
|
||||
|
||||
client->send("Hello!");
|
||||
|
||||
emitting an event to be handled by the server, argument json formatting is up to you
|
||||
|
||||
client->emit("eventname", "[{\"arg\":\"value\"}]");
|
||||
|
||||
registering an event callback, target should be a member function in a subclass of SIODelegate
|
||||
CC_CALLBACK_2 is used to wrap the callback with std::bind and store as an SIOEvent
|
||||
|
||||
client->on("eventname", CC_CALLBACK_2(TargetClass::targetfunc, *targetclass_instance));
|
||||
|
||||
event target function should match this pattern, *this pointer will be made available
|
||||
|
||||
void TargetClass::targetfunc(SIOClient *, const std::string&)
|
||||
|
||||
disconnect from the endpoint by calling disconnect(), onClose will be called on the delegate once complete
|
||||
in the onClose method the pointer should be set to NULL or used to connect to a new endpoint
|
||||
|
||||
client->disconnect();
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __CC_SOCKETIO_H__
|
||||
#define __CC_SOCKETIO_H__
|
||||
|
||||
#include "ExtensionMacros.h"
|
||||
#include "cocos2d.h"
|
||||
|
||||
NS_CC_EXT_BEGIN
|
||||
|
||||
//forward declarations
|
||||
class SIOClientImpl;
|
||||
class SIOClient;
|
||||
|
||||
/**
|
||||
* @brief Singleton and wrapper class to provide static creation method as well as registry of all sockets
|
||||
*/
|
||||
class SocketIO
|
||||
{
|
||||
public:
|
||||
SocketIO();
|
||||
virtual ~SocketIO(void);
|
||||
|
||||
static SocketIO *instance();
|
||||
|
||||
/**
|
||||
* @brief The delegate class to process socket.io events
|
||||
*/
|
||||
class SIODelegate
|
||||
{
|
||||
public:
|
||||
virtual ~SIODelegate() {}
|
||||
virtual void onConnect(SIOClient* client) = 0;
|
||||
virtual void onMessage(SIOClient* client, const std::string& data) = 0;
|
||||
virtual void onClose(SIOClient* client) = 0;
|
||||
virtual void onError(SIOClient* client, const std::string& data) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Static client creation method, similar to socketio.connect(uri) in JS
|
||||
* @param delegate The delegate which want to receive events from the socket.io client
|
||||
* @param uri The URI of the socket.io server
|
||||
* @return An initialized SIOClient if connected successfully, otherwise NULL
|
||||
*/
|
||||
static SIOClient* connect(SocketIO::SIODelegate& delegate, const std::string& uri);
|
||||
|
||||
SIOClientImpl* getSocket(const std::string& uri);
|
||||
void addSocket(const std::string& uri, SIOClientImpl* socket);
|
||||
void removeSocket(const std::string& uri);
|
||||
|
||||
private:
|
||||
|
||||
static SocketIO *_inst;
|
||||
|
||||
Dictionary* _sockets;
|
||||
|
||||
};
|
||||
|
||||
//c++11 style callbacks entities will be created using CC_CALLBACK (which uses std::bind)
|
||||
typedef std::function<void(SIOClient*, const std::string&)> SIOEvent;
|
||||
//c++11 map to callbacks
|
||||
typedef std::map<std::string, SIOEvent> EventRegistry;
|
||||
|
||||
/**
|
||||
* @brief A single connection to a socket.io endpoint
|
||||
*/
|
||||
class SIOClient
|
||||
: public Object
|
||||
{
|
||||
private:
|
||||
int _port;
|
||||
std::string _host, _path, _tag;
|
||||
bool _connected;
|
||||
SIOClientImpl* _socket;
|
||||
|
||||
SocketIO::SIODelegate* _delegate;
|
||||
|
||||
EventRegistry _eventRegistry;
|
||||
|
||||
public:
|
||||
SIOClient(const std::string& host, int port, const std::string& path, SIOClientImpl* impl, SocketIO::SIODelegate& delegate);
|
||||
virtual ~SIOClient(void);
|
||||
|
||||
SocketIO::SIODelegate* getDelegate() { return _delegate; };
|
||||
|
||||
void onOpen();
|
||||
void onConnect();
|
||||
void receivedDisconnect();
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the endpoint, onClose will be called on the delegate when comlpete
|
||||
*/
|
||||
void disconnect();
|
||||
/**
|
||||
* @brief Send a message to the socket.io server
|
||||
*/
|
||||
void send(std::string s);
|
||||
/**
|
||||
* @brief The delegate class to process socket.io events
|
||||
*/
|
||||
void emit(std::string eventname, std::string args);
|
||||
/**
|
||||
* @brief Used to resgister a socket.io event callback
|
||||
* Event argument should be passed using CC_CALLBACK2(&Base::function, this)
|
||||
*/
|
||||
void on(const std::string& eventName, SIOEvent e);
|
||||
void fireEvent(const std::string& eventName, const std::string& data);
|
||||
|
||||
inline void setTag(const char* tag)
|
||||
{
|
||||
_tag = tag;
|
||||
};
|
||||
|
||||
inline const char* getTag()
|
||||
{
|
||||
return _tag.c_str();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
NS_CC_EXT_END
|
||||
|
||||
#endif /* defined(__CC_JSB_SOCKETIO_H__) */
|
|
@ -166,6 +166,7 @@
|
|||
<ClCompile Include="..\GUI\CCScrollView\CCTableViewCell.cpp" />
|
||||
<ClCompile Include="..\LocalStorage\LocalStorage.cpp" />
|
||||
<ClCompile Include="..\network\HttpClient.cpp" />
|
||||
<ClCompile Include="..\network\SocketIO.cpp" />
|
||||
<ClCompile Include="..\network\Websocket.cpp" />
|
||||
<ClCompile Include="..\physics_nodes\CCPhysicsDebugNode.cpp" />
|
||||
<ClCompile Include="..\physics_nodes\CCPhysicsSprite.cpp" />
|
||||
|
@ -287,6 +288,7 @@
|
|||
<ClInclude Include="..\network\HttpClient.h" />
|
||||
<ClInclude Include="..\network\HttpRequest.h" />
|
||||
<ClInclude Include="..\network\HttpResponse.h" />
|
||||
<ClInclude Include="..\network\SocketIO.h" />
|
||||
<ClInclude Include="..\network\Websocket.h" />
|
||||
<ClInclude Include="..\physics_nodes\CCPhysicsDebugNode.h" />
|
||||
<ClInclude Include="..\physics_nodes\CCPhysicsSprite.h" />
|
||||
|
|
|
@ -354,6 +354,9 @@
|
|||
<ClCompile Include="..\CCArmature\external_tool\Json\lib_json\json_writer.cpp">
|
||||
<Filter>CCArmature\external_tool</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\network\SocketIO.cpp">
|
||||
<Filter>network</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\GUI\CCScrollView\CCScrollView.h">
|
||||
|
@ -712,6 +715,9 @@
|
|||
<ClInclude Include="..\CCArmature\external_tool\Json\lib_json\writer.h">
|
||||
<Filter>CCArmature\external_tool</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\network\SocketIO.h">
|
||||
<Filter>network</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\CCArmature\external_tool\Json\lib_json\json_internalarray.inl">
|
||||
|
|
|
@ -63,6 +63,7 @@ Classes/ExtensionsTest/ComponentsTest/ProjectileController.cpp \
|
|||
Classes/ExtensionsTest/ComponentsTest/SceneController.cpp \
|
||||
Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp \
|
||||
Classes/ExtensionsTest/NetworkTest/WebSocketTest.cpp \
|
||||
Classes/ExtensionsTest/NetworkTest/SocketIOTest.cpp \
|
||||
Classes/ExtensionsTest/EditBoxTest/EditBoxTest.cpp \
|
||||
Classes/ExtensionsTest/TableViewTest/TableViewTestScene.cpp \
|
||||
Classes/ExtensionsTest/TableViewTest/CustomTableViewCell.cpp \
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include "NetworkTest/WebSocketTest.h"
|
||||
#include "NetworkTest/SocketIOTest.h"
|
||||
#endif
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN)
|
||||
|
@ -61,6 +62,8 @@ static struct {
|
|||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
{ "WebSocketTest", [](Object *sender){ runWebSocketTest();}
|
||||
},
|
||||
{ "SocketIOTest", [](Object *sender){ runSocketIOTest();}
|
||||
},
|
||||
#endif
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN)
|
||||
{ "EditBoxTest", [](Object *sender){ runEditBoxTest();}
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
//
|
||||
// SocketIOTest.cpp
|
||||
// TestCpp
|
||||
//
|
||||
// Created by Chris Hannon on 6/26/13.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#include "SocketIOTest.h"
|
||||
#include "../ExtensionsTest.h"
|
||||
|
||||
USING_NS_CC;
|
||||
USING_NS_CC_EXT;
|
||||
|
||||
SocketIOTestLayer::SocketIOTestLayer(void)
|
||||
: _sioClient(NULL)
|
||||
, _sioEndpoint(NULL)
|
||||
{
|
||||
//set the clients to NULL until we are ready to connect
|
||||
|
||||
Size winSize = Director::sharedDirector()->getWinSize();
|
||||
|
||||
const int MARGIN = 40;
|
||||
const int SPACE = 35;
|
||||
|
||||
LabelTTF *label = LabelTTF::create("SocketIO Extension Test", "Arial", 28);
|
||||
label->setPosition(ccp(winSize.width / 2, winSize.height - MARGIN));
|
||||
addChild(label, 0);
|
||||
|
||||
Menu *menuRequest = Menu::create();
|
||||
menuRequest->setPosition(PointZero);
|
||||
addChild(menuRequest);
|
||||
|
||||
// Test to create basic client in the default namespace
|
||||
LabelTTF *labelSIOClient = LabelTTF::create("Open SocketIO Client", "Arial", 22);
|
||||
MenuItemLabel *itemSIOClient = MenuItemLabel::create(labelSIOClient, CC_CALLBACK_1(SocketIOTestLayer::onMenuSIOClientClicked, this));
|
||||
itemSIOClient->setPosition(ccp(VisibleRect::left().x + labelSIOClient->getContentSize().width / 2 + 5, winSize.height - MARGIN - SPACE));
|
||||
menuRequest->addChild(itemSIOClient);
|
||||
|
||||
// Test to create a client at the endpoint '/testpoint'
|
||||
LabelTTF *labelSIOEndpoint = LabelTTF::create("Open SocketIO Endpoint", "Arial", 22);
|
||||
MenuItemLabel *itemSIOEndpoint = MenuItemLabel::create(labelSIOEndpoint, CC_CALLBACK_1(SocketIOTestLayer::onMenuSIOEndpointClicked, this));
|
||||
itemSIOEndpoint->setPosition(ccp(VisibleRect::right().x - labelSIOEndpoint->getContentSize().width / 2 - 5, winSize.height - MARGIN - SPACE));
|
||||
menuRequest->addChild(itemSIOEndpoint);
|
||||
|
||||
// Test sending message to default namespace
|
||||
LabelTTF *labelTestMessage = LabelTTF::create("Send Test Message", "Arial", 22);
|
||||
MenuItemLabel *itemTestMessage = MenuItemLabel::create(labelTestMessage, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestMessageClicked, this));
|
||||
itemTestMessage->setPosition(ccp(VisibleRect::left().x + labelTestMessage->getContentSize().width / 2 + 5, winSize.height - MARGIN - 2 * SPACE));
|
||||
menuRequest->addChild(itemTestMessage);
|
||||
|
||||
// Test sending message to the endpoint '/testpoint'
|
||||
LabelTTF *labelTestMessageEndpoint = LabelTTF::create("Test Endpoint Message", "Arial", 22);
|
||||
MenuItemLabel *itemTestMessageEndpoint = MenuItemLabel::create(labelTestMessageEndpoint, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestMessageEndpointClicked, this));
|
||||
itemTestMessageEndpoint->setPosition(ccp(VisibleRect::right().x - labelTestMessageEndpoint->getContentSize().width / 2 - 5, winSize.height - MARGIN - 2 * SPACE));
|
||||
menuRequest->addChild(itemTestMessageEndpoint);
|
||||
|
||||
// Test sending event 'echotest' to default namespace
|
||||
LabelTTF *labelTestEvent = LabelTTF::create("Send Test Event", "Arial", 22);
|
||||
MenuItemLabel *itemTestEvent = MenuItemLabel::create(labelTestEvent, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestEventClicked, this));
|
||||
itemTestEvent->setPosition(ccp(VisibleRect::left().x + labelTestEvent->getContentSize().width / 2 + 5, winSize.height - MARGIN - 3 * SPACE));
|
||||
menuRequest->addChild(itemTestEvent);
|
||||
|
||||
// Test sending event 'echotest' to the endpoint '/testpoint'
|
||||
LabelTTF *labelTestEventEndpoint = LabelTTF::create("Test Endpoint Event", "Arial", 22);
|
||||
MenuItemLabel *itemTestEventEndpoint = MenuItemLabel::create(labelTestEventEndpoint, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestEventEndpointClicked, this));
|
||||
itemTestEventEndpoint->setPosition(ccp(VisibleRect::right().x - labelTestEventEndpoint->getContentSize().width / 2 - 5, winSize.height - MARGIN - 3 * SPACE));
|
||||
menuRequest->addChild(itemTestEventEndpoint);
|
||||
|
||||
// Test disconnecting basic client
|
||||
LabelTTF *labelTestClientDisconnect = LabelTTF::create("Disconnect Socket", "Arial", 22);
|
||||
MenuItemLabel *itemClientDisconnect = MenuItemLabel::create(labelTestClientDisconnect, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestClientDisconnectClicked, this));
|
||||
itemClientDisconnect->setPosition(ccp(VisibleRect::left().x + labelTestClientDisconnect->getContentSize().width / 2 + 5, winSize.height - MARGIN - 4 * SPACE));
|
||||
menuRequest->addChild(itemClientDisconnect);
|
||||
|
||||
// Test disconnecting the endpoint '/testpoint'
|
||||
LabelTTF *labelTestEndpointDisconnect = LabelTTF::create("Disconnect Endpoint", "Arial", 22);
|
||||
MenuItemLabel *itemTestEndpointDisconnect = MenuItemLabel::create(labelTestEndpointDisconnect, CC_CALLBACK_1(SocketIOTestLayer::onMenuTestEndpointDisconnectClicked, this));
|
||||
itemTestEndpointDisconnect->setPosition(ccp(VisibleRect::right().x - labelTestEndpointDisconnect->getContentSize().width / 2 - 5, winSize.height - MARGIN - 4 * SPACE));
|
||||
menuRequest->addChild(itemTestEndpointDisconnect);
|
||||
|
||||
// Sahred Status Label
|
||||
_sioClientStatus = LabelTTF::create("Not connected...", "Arial", 14, CCSizeMake(320, 100), kTextAlignmentLeft);
|
||||
_sioClientStatus->setAnchorPoint(ccp(0, 0));
|
||||
_sioClientStatus->setPosition(ccp(VisibleRect::left().x, VisibleRect::rightBottom().y));
|
||||
this->addChild(_sioClientStatus);
|
||||
|
||||
// Back Menu
|
||||
MenuItemFont *itemBack = MenuItemFont::create("Back", CC_CALLBACK_1(SocketIOTestLayer::toExtensionsMainLayer, this));
|
||||
itemBack->setPosition(ccp(VisibleRect::rightBottom().x - 50, VisibleRect::rightBottom().y + 25));
|
||||
Menu *menuBack = Menu::create(itemBack, NULL);
|
||||
menuBack->setPosition(PointZero);
|
||||
addChild(menuBack);
|
||||
|
||||
}
|
||||
|
||||
|
||||
SocketIOTestLayer::~SocketIOTestLayer(void)
|
||||
{
|
||||
}
|
||||
|
||||
//test event callback handlers, these will be registered with socket.io
|
||||
void SocketIOTestLayer::testevent(SIOClient *client, const std::string& data) {
|
||||
|
||||
CCLog("SocketIOTestLayer::testevent called with data: %s", data.c_str());
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " received event testevent with data: " << data.c_str();
|
||||
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::echotest(SIOClient *client, const std::string& data) {
|
||||
|
||||
CCLog("SocketIOTestLayer::echotest called with data: %s", data.c_str());
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " received event echotest with data: " << data.c_str();
|
||||
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::toExtensionsMainLayer(cocos2d::Object *sender)
|
||||
{
|
||||
ExtensionsTestScene *pScene = new ExtensionsTestScene();
|
||||
pScene->runThisTest();
|
||||
pScene->release();
|
||||
|
||||
if(_sioEndpoint) _sioEndpoint->disconnect();
|
||||
if(_sioClient) _sioClient->disconnect();
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuSIOClientClicked(cocos2d::Object *sender)
|
||||
{
|
||||
//create a client by using this static method, url does not need to contain the protocol
|
||||
_sioClient = SocketIO::connect(*this, "ws://channon.us:3000");
|
||||
//you may set a tag for the client for reference in callbacks
|
||||
_sioClient->setTag("Test Client");
|
||||
|
||||
//register event callbacks using the CC_CALLBACK_2() macro and passing the instance of the target class
|
||||
_sioClient->on("testevent", CC_CALLBACK_2(SocketIOTestLayer::testevent, this));
|
||||
_sioClient->on("echotest", CC_CALLBACK_2(SocketIOTestLayer::echotest, this));
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuSIOEndpointClicked(cocos2d::Object *sender)
|
||||
{
|
||||
//repeat the same connection steps for the namespace "testpoint"
|
||||
_sioEndpoint = SocketIO::connect(*this, "ws://channon.us:3000/testpoint");
|
||||
//a tag to differentiate in shared callbacks
|
||||
_sioEndpoint->setTag("Test Endpoint");
|
||||
|
||||
//demonstrating how callbacks can be shared within a delegate
|
||||
_sioEndpoint->on("testevent", CC_CALLBACK_2(SocketIOTestLayer::testevent, this));
|
||||
_sioEndpoint->on("echotest", CC_CALLBACK_2(SocketIOTestLayer::echotest, this));
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestMessageClicked(cocos2d::Object *sender)
|
||||
{
|
||||
//check that the socket is != NULL before sending or emitting events
|
||||
//the client should be NULL either before initialization and connection or after disconnect
|
||||
if(_sioClient != NULL) _sioClient->send("Hello Socket.IO!");
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestMessageEndpointClicked(cocos2d::Object *sender)
|
||||
{
|
||||
|
||||
if(_sioEndpoint != NULL) _sioEndpoint->send("Hello Socket.IO!");
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestEventClicked(cocos2d::Object *sender)
|
||||
{
|
||||
//check that the socket is != NULL before sending or emitting events
|
||||
//the client should be NULL either before initialization and connection or after disconnect
|
||||
if(_sioClient != NULL) _sioClient->emit("echotest","[{\"name\":\"myname\",\"type\":\"mytype\"}]");
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestEventEndpointClicked(cocos2d::Object *sender)
|
||||
{
|
||||
|
||||
if(_sioEndpoint != NULL) _sioEndpoint->emit("echotest","[{\"name\":\"myname\",\"type\":\"mytype\"}]");
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestClientDisconnectClicked(cocos2d::Object *sender)
|
||||
{
|
||||
|
||||
if(_sioClient != NULL) _sioClient->disconnect();
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMenuTestEndpointDisconnectClicked(cocos2d::Object *sender)
|
||||
{
|
||||
|
||||
if(_sioEndpoint != NULL) _sioEndpoint->disconnect();
|
||||
|
||||
}
|
||||
|
||||
// Delegate methods
|
||||
|
||||
void SocketIOTestLayer::onConnect(cocos2d::extension::SIOClient* client)
|
||||
{
|
||||
CCLog("SocketIOTestLayer::onConnect called");
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " connected!";
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onMessage(cocos2d::extension::SIOClient* client, const std::string& data)
|
||||
{
|
||||
CCLog("SocketIOTestLayer::onMessage received: %s", data.c_str());
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " received message with content: " << data.c_str();
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onClose(cocos2d::extension::SIOClient* client)
|
||||
{
|
||||
CCLog("SocketIOTestLayer::onClose called");
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " closed!";
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
|
||||
//set the local pointer to NULL or connect to another client
|
||||
//the client object will be released on its own after this method completes
|
||||
if(client == _sioClient) {
|
||||
|
||||
_sioClient = NULL;
|
||||
} else if(client == _sioEndpoint) {
|
||||
|
||||
_sioEndpoint = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SocketIOTestLayer::onError(cocos2d::extension::SIOClient* client, const std::string& data)
|
||||
{
|
||||
CCLog("SocketIOTestLayer::onError received: %s", data.c_str());
|
||||
|
||||
std::stringstream s;
|
||||
s << client->getTag() << " received error with content: " << data.c_str();
|
||||
_sioClientStatus->setString(s.str().c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void runSocketIOTest()
|
||||
{
|
||||
Scene *pScene = Scene::create();
|
||||
SocketIOTestLayer *pLayer = new SocketIOTestLayer();
|
||||
pScene->addChild(pLayer);
|
||||
|
||||
Director::sharedDirector()->replaceScene(pScene);
|
||||
pLayer->release();
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// SocketIOTest.h
|
||||
// TestCpp
|
||||
//
|
||||
// Created by Chris Hannon on 6/26/13.
|
||||
//
|
||||
//
|
||||
#ifndef __TestCpp__SocketIOTest__
|
||||
#define __TestCpp__SocketIOTest__
|
||||
|
||||
#include "cocos2d.h"
|
||||
#include "cocos-ext.h"
|
||||
#include "network/SocketIO.h"
|
||||
|
||||
class SocketIOTestLayer
|
||||
: public cocos2d::Layer
|
||||
, public cocos2d::extension::SocketIO::SIODelegate
|
||||
{
|
||||
public:
|
||||
SocketIOTestLayer(void);
|
||||
virtual ~SocketIOTestLayer(void);
|
||||
|
||||
virtual void onConnect(cocos2d::extension::SIOClient* client);
|
||||
virtual void onMessage(cocos2d::extension::SIOClient* client, const std::string& data);
|
||||
virtual void onClose(cocos2d::extension::SIOClient* client);
|
||||
virtual void onError(cocos2d::extension::SIOClient* client, const std::string& data);
|
||||
|
||||
void toExtensionsMainLayer(cocos2d::Object *sender);
|
||||
|
||||
void onMenuSIOClientClicked(cocos2d::Object *sender);
|
||||
void onMenuTestMessageClicked(cocos2d::Object *sender);
|
||||
void onMenuTestEventClicked(cocos2d::Object *sender);
|
||||
void onMenuTestClientDisconnectClicked(cocos2d::Object *sender);
|
||||
|
||||
void onMenuSIOEndpointClicked(cocos2d::Object *sender);
|
||||
void onMenuTestMessageEndpointClicked(cocos2d::Object *sender);
|
||||
void onMenuTestEventEndpointClicked(cocos2d::Object *sender);
|
||||
void onMenuTestEndpointDisconnectClicked(cocos2d::Object *sender);
|
||||
|
||||
|
||||
void testevent(cocos2d::extension::SIOClient *client, const std::string& data);
|
||||
void echotest(cocos2d::extension::SIOClient *client, const std::string& data);
|
||||
|
||||
cocos2d::extension::SIOClient *_sioClient, *_sioEndpoint;
|
||||
|
||||
cocos2d::LabelTTF *_sioClientStatus;
|
||||
};
|
||||
|
||||
void runSocketIOTest();
|
||||
|
||||
#endif /* defined(__TestCpp__SocketIOTest__) */
|
|
@ -150,6 +150,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\..\..\external\libwebsockets\win32\lib\*.*" "$(O
|
|||
<ClCompile Include="..\Classes\ExtensionsTest\ControlExtensionTest\CCControlStepperTest\CCControlStepperTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\EditBoxTest\EditBoxTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\NetworkTest\HttpClientTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\NetworkTest\SocketIOTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\NetworkTest\WebSocketTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\Scale9SpriteTest\Scale9SpriteTest.cpp" />
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\TableViewTest\CustomTableViewCell.cpp" />
|
||||
|
@ -258,6 +259,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\..\..\external\libwebsockets\win32\lib\*.*" "$(O
|
|||
<ClInclude Include="..\Classes\ExtensionsTest\ControlExtensionTest\CCControlStepperTest\CCControlStepperTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\EditBoxTest\EditBoxTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\NetworkTest\HttpClientTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\NetworkTest\SocketIOTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\NetworkTest\WebSocketTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\Scale9SpriteTest\Scale9SpriteTest.h" />
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\TableViewTest\CustomTableViewCell.h" />
|
||||
|
|
|
@ -549,6 +549,9 @@
|
|||
<ClCompile Include="..\Classes\ExtensionsTest\Scale9SpriteTest\Scale9SpriteTest.cpp">
|
||||
<Filter>Classes\ExtensionsTest\Scale9SpriteTest</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Classes\ExtensionsTest\NetworkTest\SocketIOTest.cpp">
|
||||
<Filter>Classes\ExtensionsTest\NetworkTest</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="main.h">
|
||||
|
@ -1043,5 +1046,8 @@
|
|||
<ClInclude Include="..\Classes\ExtensionsTest\Scale9SpriteTest\Scale9SpriteTest.h">
|
||||
<Filter>Classes\ExtensionsTest\Scale9SpriteTest</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Classes\ExtensionsTest\NetworkTest\SocketIOTest.h">
|
||||
<Filter>Classes\ExtensionsTest\NetworkTest</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue