2013-05-31 23:13:03 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2010-2013 cocos2d-x.org
|
|
|
|
Copyright (c) 2013 James Chen
|
|
|
|
|
|
|
|
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.
|
2013-06-06 14:16:57 +08:00
|
|
|
|
|
|
|
"[WebSocket module] is based in part on the work of the libwebsockets project
|
|
|
|
(http://libwebsockets.org)"
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "WebSocket.h"
|
2013-06-25 12:51:44 +08:00
|
|
|
#include <thread>
|
|
|
|
#include <mutex>
|
2013-05-31 23:13:03 +08:00
|
|
|
#include <queue>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
NS_CC_EXT_BEGIN
|
|
|
|
|
|
|
|
class WsMessage
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
WsMessage() : what(0), obj(NULL){}
|
|
|
|
unsigned int what; // message type
|
|
|
|
void* obj;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Websocket thread helper, it's used for sending message between UI thread and websocket thread.
|
|
|
|
*/
|
2013-06-20 14:15:53 +08:00
|
|
|
class WsThreadHelper : public cocos2d::Object
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
WsThreadHelper();
|
|
|
|
~WsThreadHelper();
|
|
|
|
|
|
|
|
// Creates a new thread
|
|
|
|
bool createThread(const WebSocket& ws);
|
|
|
|
// Quits sub-thread (websocket thread).
|
|
|
|
void quitSubThread();
|
|
|
|
|
|
|
|
// Schedule callback function
|
|
|
|
virtual void update(float dt);
|
|
|
|
|
|
|
|
// Sends message to UI thread. It's needed to be invoked in sub-thread.
|
|
|
|
void sendMessageToUIThread(WsMessage *msg);
|
|
|
|
|
|
|
|
// Sends message to sub-thread(websocket thread). It's needs to be invoked in UI thread.
|
|
|
|
void sendMessageToSubThread(WsMessage *msg);
|
|
|
|
|
|
|
|
// Waits the sub-thread (websocket thread) to exit,
|
|
|
|
void joinSubThread();
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
2013-06-25 12:51:44 +08:00
|
|
|
void wsThreadEntryFunc();
|
2013-05-31 23:13:03 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
std::list<WsMessage*>* _UIWsMessageQueue;
|
|
|
|
std::list<WsMessage*>* _subThreadWsMessageQueue;
|
2013-06-25 12:51:44 +08:00
|
|
|
std::mutex _UIWsMessageQueueMutex;
|
|
|
|
std::mutex _subThreadWsMessageQueueMutex;
|
|
|
|
std::thread* _subThreadInstance;
|
2013-05-31 23:13:03 +08:00
|
|
|
WebSocket* _ws;
|
|
|
|
bool _needQuit;
|
|
|
|
friend class WebSocket;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Wrapper for converting websocket callback from static function to member function of WebSocket class.
|
|
|
|
class WebSocketCallbackWrapper {
|
|
|
|
public:
|
|
|
|
|
|
|
|
static int onSocketCallback(struct libwebsocket_context *ctx,
|
|
|
|
struct libwebsocket *wsi,
|
|
|
|
enum libwebsocket_callback_reasons reason,
|
|
|
|
void *user, void *in, size_t len)
|
|
|
|
{
|
|
|
|
// Gets the user data from context. We know that it's a 'WebSocket' instance.
|
|
|
|
WebSocket* wsInstance = (WebSocket*)libwebsocket_context_user(ctx);
|
|
|
|
if (wsInstance)
|
|
|
|
{
|
|
|
|
return wsInstance->onSocketCallback(ctx, wsi, reason, user, in, len);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Implementation of WsThreadHelper
|
|
|
|
WsThreadHelper::WsThreadHelper()
|
2013-06-25 12:51:44 +08:00
|
|
|
: _subThreadInstance(nullptr)
|
|
|
|
, _ws(NULL)
|
2013-05-31 23:13:03 +08:00
|
|
|
, _needQuit(false)
|
|
|
|
{
|
|
|
|
_UIWsMessageQueue = new std::list<WsMessage*>();
|
|
|
|
_subThreadWsMessageQueue = new std::list<WsMessage*>();
|
|
|
|
|
2013-07-12 06:24:23 +08:00
|
|
|
Director::getInstance()->getScheduler()->scheduleUpdateForTarget(this, 0, false);
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
WsThreadHelper::~WsThreadHelper()
|
|
|
|
{
|
2013-07-12 06:24:23 +08:00
|
|
|
Director::getInstance()->getScheduler()->unscheduleAllForTarget(this);
|
2013-06-26 10:10:23 +08:00
|
|
|
joinSubThread();
|
2013-06-25 12:51:44 +08:00
|
|
|
CC_SAFE_DELETE(_subThreadInstance);
|
2013-05-31 23:13:03 +08:00
|
|
|
delete _UIWsMessageQueue;
|
|
|
|
delete _subThreadWsMessageQueue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WsThreadHelper::createThread(const WebSocket& ws)
|
|
|
|
{
|
|
|
|
_ws = const_cast<WebSocket*>(&ws);
|
|
|
|
|
|
|
|
// Creates websocket thread
|
2013-06-25 12:51:44 +08:00
|
|
|
_subThreadInstance = new std::thread(&WsThreadHelper::wsThreadEntryFunc, this);
|
|
|
|
return true;
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void WsThreadHelper::quitSubThread()
|
|
|
|
{
|
|
|
|
_needQuit = true;
|
|
|
|
}
|
|
|
|
|
2013-06-25 12:51:44 +08:00
|
|
|
void WsThreadHelper::wsThreadEntryFunc()
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
_ws->onSubThreadStarted();
|
|
|
|
|
|
|
|
while (!_needQuit)
|
|
|
|
{
|
|
|
|
if (_ws->onSubThreadLoop())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ws->onSubThreadEnded();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WsThreadHelper::sendMessageToUIThread(WsMessage *msg)
|
|
|
|
{
|
2013-06-26 10:10:23 +08:00
|
|
|
std::lock_guard<std::mutex> lk(_UIWsMessageQueueMutex);
|
2013-05-31 23:13:03 +08:00
|
|
|
_UIWsMessageQueue->push_back(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WsThreadHelper::sendMessageToSubThread(WsMessage *msg)
|
|
|
|
{
|
2013-06-26 10:10:23 +08:00
|
|
|
std::lock_guard<std::mutex> lk(_subThreadWsMessageQueueMutex);
|
2013-05-31 23:13:03 +08:00
|
|
|
_subThreadWsMessageQueue->push_back(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WsThreadHelper::joinSubThread()
|
|
|
|
{
|
2013-06-25 12:51:44 +08:00
|
|
|
if (_subThreadInstance->joinable())
|
|
|
|
{
|
|
|
|
_subThreadInstance->join();
|
|
|
|
}
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void WsThreadHelper::update(float dt)
|
|
|
|
{
|
|
|
|
WsMessage *msg = NULL;
|
|
|
|
|
|
|
|
// Returns quickly if no message
|
2013-06-26 10:10:23 +08:00
|
|
|
std::lock_guard<std::mutex> lk(_UIWsMessageQueueMutex);
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
if (0 == _UIWsMessageQueue->size())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets message
|
|
|
|
msg = *(_UIWsMessageQueue->begin());
|
|
|
|
_UIWsMessageQueue->pop_front();
|
|
|
|
|
|
|
|
if (_ws)
|
|
|
|
{
|
|
|
|
_ws->onUIThreadReceiveMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
CC_SAFE_DELETE(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum WS_MSG {
|
|
|
|
WS_MSG_TO_SUBTRHEAD_SENDING_STRING = 0,
|
|
|
|
WS_MSG_TO_SUBTRHEAD_SENDING_BINARY,
|
|
|
|
WS_MSG_TO_UITHREAD_OPEN,
|
|
|
|
WS_MSG_TO_UITHREAD_MESSAGE,
|
|
|
|
WS_MSG_TO_UITHREAD_ERROR,
|
|
|
|
WS_MSG_TO_UITHREAD_CLOSE
|
|
|
|
};
|
|
|
|
|
|
|
|
WebSocket::WebSocket()
|
2013-06-03 09:55:43 +08:00
|
|
|
: _readyState(kStateConnecting)
|
2013-05-31 23:13:03 +08:00
|
|
|
, _port(80)
|
|
|
|
, _wsHelper(NULL)
|
|
|
|
, _wsInstance(NULL)
|
|
|
|
, _wsContext(NULL)
|
|
|
|
, _delegate(NULL)
|
|
|
|
, _SSLConnection(0)
|
|
|
|
, _wsProtocols(NULL)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
WebSocket::~WebSocket()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
CC_SAFE_RELEASE_NULL(_wsHelper);
|
|
|
|
|
|
|
|
for (int i = 0; _wsProtocols[i].callback != NULL; ++i) {
|
|
|
|
CC_SAFE_DELETE_ARRAY(_wsProtocols[i].name);
|
|
|
|
}
|
|
|
|
CC_SAFE_DELETE_ARRAY(_wsProtocols);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WebSocket::init(const Delegate& delegate,
|
|
|
|
const std::string& url,
|
|
|
|
const std::vector<std::string>* protocols/* = NULL*/)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
bool useSSL = false;
|
|
|
|
std::string host = url;
|
|
|
|
int pos = 0;
|
|
|
|
int port = 80;
|
|
|
|
|
|
|
|
_delegate = const_cast<Delegate*>(&delegate);
|
|
|
|
|
|
|
|
//ws://
|
|
|
|
pos = host.find("ws://");
|
|
|
|
if (pos == 0){
|
|
|
|
host.erase(0,5);
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = host.find("wss://");
|
|
|
|
if (pos == 0)
|
|
|
|
{
|
|
|
|
host.erase(0,6);
|
|
|
|
useSSL = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = host.find(":");
|
|
|
|
if(pos >= 0){
|
|
|
|
port = atoi(host.substr(pos+1, host.size()).c_str());
|
|
|
|
}
|
|
|
|
|
2013-06-17 18:28:12 +08:00
|
|
|
pos = host.find("/", 0);
|
2013-05-31 23:13:03 +08:00
|
|
|
std::string path = "/";
|
|
|
|
if(pos >= 0){
|
|
|
|
path += host.substr(pos + 1, host.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = host.find(":");
|
|
|
|
if(pos >= 0){
|
|
|
|
host.erase(pos, host.size());
|
2013-06-17 18:28:12 +08:00
|
|
|
}else if((pos = host.find("/"))>=0) {
|
|
|
|
host.erase(pos, host.size());
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
_host = host;
|
|
|
|
_port = port;
|
|
|
|
_path = path;
|
|
|
|
_SSLConnection = useSSL ? 1 : 0;
|
|
|
|
|
2013-06-17 18:28:12 +08:00
|
|
|
CCLOG("[WebSocket::init] _host: %s, _port: %d, _path: %s", _host.c_str(), _port, _path.c_str());
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
int protocolCount = 0;
|
|
|
|
if (protocols && protocols->size() > 0)
|
|
|
|
{
|
|
|
|
protocolCount = protocols->size();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
protocolCount = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
_wsProtocols = new libwebsocket_protocols[protocolCount+1];
|
|
|
|
memset(_wsProtocols, 0, sizeof(libwebsocket_protocols)*(protocolCount+1));
|
|
|
|
|
|
|
|
if (protocols)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
for (std::vector<std::string>::const_iterator iter = protocols->begin(); iter != protocols->end(); ++iter, ++i) {
|
|
|
|
char* name = new char[(*iter).length()+1];
|
|
|
|
strcpy(name, (*iter).c_str());
|
|
|
|
_wsProtocols[i].name = name;
|
|
|
|
_wsProtocols[i].callback = WebSocketCallbackWrapper::onSocketCallback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char* name = new char[20];
|
|
|
|
strcpy(name, "default-protocol");
|
|
|
|
_wsProtocols[0].name = name;
|
|
|
|
_wsProtocols[0].callback = WebSocketCallbackWrapper::onSocketCallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WebSocket thread needs to be invoked at the end of this method.
|
|
|
|
_wsHelper = new WsThreadHelper();
|
|
|
|
ret = _wsHelper->createThread(*this);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::send(const std::string& message)
|
|
|
|
{
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_readyState == kStateOpen)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
// In main thread
|
|
|
|
WsMessage* msg = new WsMessage();
|
|
|
|
msg->what = WS_MSG_TO_SUBTRHEAD_SENDING_STRING;
|
|
|
|
Data* data = new Data();
|
|
|
|
data->bytes = new char[message.length()+1];
|
|
|
|
strcpy(data->bytes, message.c_str());
|
|
|
|
data->len = message.length();
|
|
|
|
msg->obj = data;
|
|
|
|
_wsHelper->sendMessageToSubThread(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::send(const unsigned char* binaryMsg, unsigned int len)
|
|
|
|
{
|
|
|
|
CCAssert(binaryMsg != NULL && len > 0, "parameter invalid.");
|
|
|
|
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_readyState == kStateOpen)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
// In main thread
|
|
|
|
WsMessage* msg = new WsMessage();
|
|
|
|
msg->what = WS_MSG_TO_SUBTRHEAD_SENDING_BINARY;
|
|
|
|
Data* data = new Data();
|
|
|
|
data->bytes = new char[len];
|
|
|
|
memcpy((void*)data->bytes, (void*)binaryMsg, len);
|
|
|
|
data->len = len;
|
|
|
|
msg->obj = data;
|
|
|
|
_wsHelper->sendMessageToSubThread(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::close()
|
|
|
|
{
|
2013-07-12 06:24:23 +08:00
|
|
|
Director::getInstance()->getScheduler()->unscheduleAllForTarget(_wsHelper);
|
2013-05-31 23:13:03 +08:00
|
|
|
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_readyState == kStateClosing || _readyState == kStateClosed)
|
2013-06-25 12:51:44 +08:00
|
|
|
{
|
2013-05-31 23:13:03 +08:00
|
|
|
return;
|
2013-06-25 12:51:44 +08:00
|
|
|
}
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
CCLOG("websocket (%p) connection closed by client", this);
|
2013-06-03 09:55:43 +08:00
|
|
|
_readyState = kStateClosed;
|
2013-05-31 23:13:03 +08:00
|
|
|
|
|
|
|
_wsHelper->joinSubThread();
|
|
|
|
|
|
|
|
// onClose callback needs to be invoked at the end of this method
|
|
|
|
// since websocket instance may be deleted in 'onClose'.
|
|
|
|
_delegate->onClose(this);
|
|
|
|
}
|
|
|
|
|
2013-06-03 09:55:43 +08:00
|
|
|
WebSocket::State WebSocket::getReadyState()
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
return _readyState;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WebSocket::onSubThreadLoop()
|
|
|
|
{
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_readyState == kStateClosed || _readyState == kStateClosing)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
libwebsocket_context_destroy(_wsContext);
|
|
|
|
// return 1 to exit the loop.
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_wsContext && _readyState != kStateClosed && _readyState != kStateClosing)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
libwebsocket_service(_wsContext, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sleep 50 ms
|
2013-06-26 11:34:04 +08:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
// return 0 to continue the loop.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::onSubThreadStarted()
|
|
|
|
{
|
|
|
|
struct lws_context_creation_info info;
|
|
|
|
memset(&info, 0, sizeof info);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create the websocket context. This tracks open connections and
|
|
|
|
* knows how to route any traffic and which protocol version to use,
|
|
|
|
* and if each connection is client or server side.
|
|
|
|
*
|
|
|
|
* For this client-only demo, we tell it to not listen on any port.
|
|
|
|
*/
|
|
|
|
|
|
|
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
|
|
|
info.protocols = _wsProtocols;
|
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
|
|
|
info.extensions = libwebsocket_get_internal_extensions();
|
|
|
|
#endif
|
|
|
|
info.gid = -1;
|
|
|
|
info.uid = -1;
|
|
|
|
info.user = (void*)this;
|
|
|
|
|
|
|
|
_wsContext = libwebsocket_create_context(&info);
|
|
|
|
|
|
|
|
if(NULL != _wsContext){
|
2013-06-03 09:55:43 +08:00
|
|
|
_readyState = kStateConnecting;
|
2013-05-31 23:13:03 +08:00
|
|
|
std::string name;
|
|
|
|
for (int i = 0; _wsProtocols[i].callback != NULL; ++i) {
|
|
|
|
name += (_wsProtocols[i].name);
|
|
|
|
if (_wsProtocols[i+1].callback != NULL)
|
|
|
|
{
|
|
|
|
name += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_wsInstance = libwebsocket_client_connect(_wsContext, _host.c_str(), _port, _SSLConnection,
|
|
|
|
_path.c_str(), _host.c_str(), _host.c_str(),
|
|
|
|
name.c_str(), -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::onSubThreadEnded()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int WebSocket::onSocketCallback(struct libwebsocket_context *ctx,
|
|
|
|
struct libwebsocket *wsi,
|
|
|
|
enum libwebsocket_callback_reasons reason,
|
|
|
|
void *user, void *in, size_t len)
|
|
|
|
{
|
|
|
|
//CCLOG("socket callback for %d reason", reason);
|
|
|
|
CCAssert(_wsContext == NULL || ctx == _wsContext, "Invalid context.");
|
|
|
|
CCAssert(_wsInstance == NULL || wsi == NULL || wsi == _wsInstance, "Invaild websocket instance.");
|
|
|
|
|
|
|
|
switch (reason)
|
|
|
|
{
|
|
|
|
case LWS_CALLBACK_DEL_POLL_FD:
|
|
|
|
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
|
|
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
|
|
|
{
|
2013-06-10 11:43:43 +08:00
|
|
|
WsMessage* msg = NULL;
|
2013-05-31 23:13:03 +08:00
|
|
|
if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR
|
2013-06-03 09:55:43 +08:00
|
|
|
|| (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == kStateConnecting)
|
|
|
|
|| (reason == LWS_CALLBACK_DEL_POLL_FD && _readyState == kStateConnecting)
|
2013-05-31 23:13:03 +08:00
|
|
|
)
|
|
|
|
{
|
2013-06-10 11:43:43 +08:00
|
|
|
msg = new WsMessage();
|
2013-05-31 23:13:03 +08:00
|
|
|
msg->what = WS_MSG_TO_UITHREAD_ERROR;
|
2013-06-03 09:55:43 +08:00
|
|
|
_readyState = kStateClosing;
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
2013-06-03 09:55:43 +08:00
|
|
|
else if (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == kStateClosing)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
2013-06-10 11:43:43 +08:00
|
|
|
msg = new WsMessage();
|
2013-05-31 23:13:03 +08:00
|
|
|
msg->what = WS_MSG_TO_UITHREAD_CLOSE;
|
|
|
|
}
|
2013-06-10 11:43:43 +08:00
|
|
|
|
|
|
|
if (msg)
|
|
|
|
{
|
|
|
|
_wsHelper->sendMessageToUIThread(msg);
|
|
|
|
}
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
|
|
|
{
|
|
|
|
WsMessage* msg = new WsMessage();
|
|
|
|
msg->what = WS_MSG_TO_UITHREAD_OPEN;
|
2013-06-03 09:55:43 +08:00
|
|
|
_readyState = kStateOpen;
|
2013-05-31 23:13:03 +08:00
|
|
|
/*
|
|
|
|
* start the ball rolling,
|
|
|
|
* LWS_CALLBACK_CLIENT_WRITEABLE will come next service
|
|
|
|
*/
|
|
|
|
libwebsocket_callback_on_writable(ctx, wsi);
|
|
|
|
_wsHelper->sendMessageToUIThread(msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
|
|
|
{
|
2013-06-26 10:10:23 +08:00
|
|
|
std::lock_guard<std::mutex> lk(_wsHelper->_subThreadWsMessageQueueMutex);
|
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
std::list<WsMessage*>::iterator iter = _wsHelper->_subThreadWsMessageQueue->begin();
|
|
|
|
|
|
|
|
int bytesWrite = 0;
|
|
|
|
for (; iter != _wsHelper->_subThreadWsMessageQueue->end(); ++iter) {
|
|
|
|
|
|
|
|
WsMessage* subThreadMsg = *iter;
|
|
|
|
|
|
|
|
if ( WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what
|
|
|
|
|| WS_MSG_TO_SUBTRHEAD_SENDING_BINARY == subThreadMsg->what)
|
|
|
|
{
|
|
|
|
Data* data = (Data*)subThreadMsg->obj;
|
|
|
|
|
|
|
|
unsigned char* buf = new unsigned char[LWS_SEND_BUFFER_PRE_PADDING
|
|
|
|
+ data->len + LWS_SEND_BUFFER_POST_PADDING];
|
|
|
|
|
|
|
|
memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, data->len);
|
|
|
|
memcpy((char*)&buf[LWS_SEND_BUFFER_PRE_PADDING], data->bytes, data->len);
|
|
|
|
|
|
|
|
enum libwebsocket_write_protocol writeProtocol;
|
|
|
|
|
|
|
|
if (WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what)
|
|
|
|
{
|
|
|
|
writeProtocol = LWS_WRITE_TEXT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writeProtocol = LWS_WRITE_BINARY;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesWrite = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], data->len, writeProtocol);
|
|
|
|
|
|
|
|
if (bytesWrite < 0) {
|
|
|
|
CCLOGERROR("%s", "libwebsocket_write error...");
|
|
|
|
}
|
|
|
|
if (bytesWrite < data->len) {
|
|
|
|
CCLOGERROR("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
CC_SAFE_DELETE_ARRAY(data->bytes);
|
|
|
|
CC_SAFE_DELETE(data);
|
|
|
|
CC_SAFE_DELETE_ARRAY(buf);
|
|
|
|
}
|
2013-06-09 14:50:51 +08:00
|
|
|
|
|
|
|
CC_SAFE_DELETE(subThreadMsg);
|
2013-05-31 23:13:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
_wsHelper->_subThreadWsMessageQueue->clear();
|
|
|
|
|
|
|
|
|
|
|
|
/* get notified as soon as we can write again */
|
|
|
|
|
|
|
|
libwebsocket_callback_on_writable(ctx, wsi);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_CLOSED:
|
|
|
|
{
|
|
|
|
|
|
|
|
CCLOG("%s", "connection closing..");
|
|
|
|
|
|
|
|
_wsHelper->quitSubThread();
|
|
|
|
|
2013-06-03 09:55:43 +08:00
|
|
|
if (_readyState != kStateClosed)
|
2013-05-31 23:13:03 +08:00
|
|
|
{
|
|
|
|
WsMessage* msg = new WsMessage();
|
2013-06-03 09:55:43 +08:00
|
|
|
_readyState = kStateClosed;
|
2013-05-31 23:13:03 +08:00
|
|
|
msg->what = WS_MSG_TO_UITHREAD_CLOSE;
|
|
|
|
_wsHelper->sendMessageToUIThread(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_CLIENT_RECEIVE:
|
|
|
|
{
|
|
|
|
if (in && len > 0)
|
|
|
|
{
|
|
|
|
WsMessage* msg = new WsMessage();
|
|
|
|
msg->what = WS_MSG_TO_UITHREAD_MESSAGE;
|
|
|
|
|
|
|
|
char* bytes = NULL;
|
|
|
|
Data* data = new Data();
|
|
|
|
|
|
|
|
if (lws_frame_is_binary(wsi))
|
|
|
|
{
|
|
|
|
|
|
|
|
bytes = new char[len];
|
|
|
|
data->isBinary = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bytes = new char[len+1];
|
|
|
|
bytes[len] = '\0';
|
|
|
|
data->isBinary = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(bytes, in, len);
|
|
|
|
|
|
|
|
data->bytes = bytes;
|
|
|
|
data->len = len;
|
|
|
|
msg->obj = (void*)data;
|
|
|
|
|
|
|
|
_wsHelper->sendMessageToUIThread(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocket::onUIThreadReceiveMessage(WsMessage* msg)
|
|
|
|
{
|
|
|
|
switch (msg->what) {
|
|
|
|
case WS_MSG_TO_UITHREAD_OPEN:
|
|
|
|
{
|
|
|
|
_delegate->onOpen(this);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WS_MSG_TO_UITHREAD_MESSAGE:
|
|
|
|
{
|
|
|
|
Data* data = (Data*)msg->obj;
|
|
|
|
_delegate->onMessage(this, *data);
|
|
|
|
CC_SAFE_DELETE_ARRAY(data->bytes);
|
|
|
|
CC_SAFE_DELETE(data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WS_MSG_TO_UITHREAD_CLOSE:
|
|
|
|
{
|
|
|
|
_delegate->onClose(this);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WS_MSG_TO_UITHREAD_ERROR:
|
|
|
|
{
|
2013-06-03 09:55:43 +08:00
|
|
|
// FIXME: The exact error needs to be checked.
|
|
|
|
WebSocket::ErrorCode err = kErrorConnectionFailure;
|
2013-05-31 23:13:03 +08:00
|
|
|
_delegate->onError(this, err);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-06-07 21:23:15 +08:00
|
|
|
|
2013-05-31 23:13:03 +08:00
|
|
|
NS_CC_EXT_END
|