mirror of https://github.com/axmolengine/axmol.git
Merge commit 'refs/pull/14867/head' of https://github.com/cocos2d/cocos2d-x into v3_libwebsockets
This commit is contained in:
commit
c6bfe24d5b
|
@ -61,6 +61,7 @@ THE SOFTWARE.
|
||||||
#include "base/CCConfiguration.h"
|
#include "base/CCConfiguration.h"
|
||||||
#include "base/CCAsyncTaskPool.h"
|
#include "base/CCAsyncTaskPool.h"
|
||||||
#include "platform/CCApplication.h"
|
#include "platform/CCApplication.h"
|
||||||
|
#include "network/WebSocket.h"
|
||||||
|
|
||||||
#if CC_ENABLE_SCRIPT_BINDING
|
#if CC_ENABLE_SCRIPT_BINDING
|
||||||
#include "CCScriptSupport.h"
|
#include "CCScriptSupport.h"
|
||||||
|
@ -930,6 +931,9 @@ void Director::reset()
|
||||||
_runningScene = nullptr;
|
_runningScene = nullptr;
|
||||||
_nextScene = nullptr;
|
_nextScene = nullptr;
|
||||||
|
|
||||||
|
// Close all websocket connection. It has to be invoked before cleaning scheduler
|
||||||
|
network::WebSocket::closeAllConnections();
|
||||||
|
|
||||||
// cleanup scheduler
|
// cleanup scheduler
|
||||||
getScheduler()->unscheduleAll();
|
getScheduler()->unscheduleAll();
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,13 +32,15 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory> // for std::shared_ptr
|
||||||
|
|
||||||
#include "platform/CCPlatformMacros.h"
|
#include "platform/CCPlatformMacros.h"
|
||||||
#include "platform/CCStdC.h"
|
#include "platform/CCStdC.h"
|
||||||
|
|
||||||
struct libwebsocket;
|
struct lws;
|
||||||
struct libwebsocket_context;
|
struct lws_context;
|
||||||
struct libwebsocket_protocols;
|
struct lws_protocols;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @addtogroup network
|
* @addtogroup network
|
||||||
|
@ -50,14 +52,20 @@ NS_CC_BEGIN
|
||||||
namespace network {
|
namespace network {
|
||||||
|
|
||||||
class WsThreadHelper;
|
class WsThreadHelper;
|
||||||
class WsMessage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket is wrapper of the libwebsockets-protocol, let the develop could call the websocket easily.
|
* WebSocket is wrapper of the libwebsockets-protocol, let the develop could call the websocket easily.
|
||||||
|
* Please note that all public methods of WebSocket have to be invoked on Cocos Thread.
|
||||||
*/
|
*/
|
||||||
class CC_DLL WebSocket
|
class CC_DLL WebSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Close all connections and wait for all websocket threads to exit
|
||||||
|
* @note This method has to be invoked on Cocos Thread
|
||||||
|
*/
|
||||||
|
static void closeAllConnections();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of WebSocket.
|
* Constructor of WebSocket.
|
||||||
*
|
*
|
||||||
|
@ -77,10 +85,11 @@ public:
|
||||||
*/
|
*/
|
||||||
struct Data
|
struct Data
|
||||||
{
|
{
|
||||||
Data():bytes(nullptr), len(0), issued(0), isBinary(false){}
|
Data():bytes(nullptr), len(0), issued(0), isBinary(false), ext(nullptr){}
|
||||||
char* bytes;
|
char* bytes;
|
||||||
ssize_t len, issued;
|
ssize_t len, issued;
|
||||||
bool isBinary;
|
bool isBinary;
|
||||||
|
void* ext;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,9 +188,18 @@ public:
|
||||||
void send(const unsigned char* binaryMsg, unsigned int len);
|
void send(const unsigned char* binaryMsg, unsigned int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes the connection to server.
|
* @brief Closes the connection to server synchronously.
|
||||||
|
* @note It's a synchronous method, it will not return until websocket thread exits.
|
||||||
*/
|
*/
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes the connection to server asynchronously.
|
||||||
|
* @note It's an asynchronous method, it just notifies websocket thread to exit and returns directly,
|
||||||
|
* If using 'closeAsync' to close websocket connection,
|
||||||
|
* be carefull of not using destructed variables in the callback of 'onClose'.
|
||||||
|
*/
|
||||||
|
void closeAsync();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets current state of connection.
|
* @brief Gets current state of connection.
|
||||||
|
@ -190,36 +208,38 @@ public:
|
||||||
State getReadyState();
|
State getReadyState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void onSubThreadStarted();
|
void onSubThreadStarted();
|
||||||
virtual int onSubThreadLoop();
|
void onSubThreadLoop();
|
||||||
virtual void onSubThreadEnded();
|
void onSubThreadEnded();
|
||||||
virtual void onUIThreadReceiveMessage(WsMessage* msg);
|
|
||||||
|
|
||||||
|
// The following callback functions are invoked in websocket thread
|
||||||
|
int onSocketCallback(struct lws *wsi, int reason, void *user, void *in, ssize_t len);
|
||||||
|
|
||||||
friend class WebSocketCallbackWrapper;
|
void onClientWritable();
|
||||||
int onSocketCallback(struct libwebsocket_context *ctx,
|
void onClientReceivedData(void* in, ssize_t len);
|
||||||
struct libwebsocket *wsi,
|
void onConnectionOpened();
|
||||||
int reason,
|
void onConnectionError();
|
||||||
void *user, void *in, ssize_t len);
|
void onConnectionClosed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::mutex _readStateMutex;
|
||||||
State _readyState;
|
State _readyState;
|
||||||
std::string _host;
|
std::string _host;
|
||||||
unsigned int _port;
|
unsigned int _port;
|
||||||
std::string _path;
|
std::string _path;
|
||||||
|
|
||||||
ssize_t _pendingFrameDataLen;
|
std::vector<char> _receivedData;
|
||||||
ssize_t _currentDataLen;
|
|
||||||
char *_currentData;
|
|
||||||
|
|
||||||
friend class WsThreadHelper;
|
friend class WsThreadHelper;
|
||||||
|
friend class WebSocketCallbackWrapper;
|
||||||
WsThreadHelper* _wsHelper;
|
WsThreadHelper* _wsHelper;
|
||||||
|
|
||||||
struct libwebsocket* _wsInstance;
|
struct lws* _wsInstance;
|
||||||
struct libwebsocket_context* _wsContext;
|
struct lws_context* _wsContext;
|
||||||
|
std::shared_ptr<bool> _isDestroyed;
|
||||||
Delegate* _delegate;
|
Delegate* _delegate;
|
||||||
int _SSLConnection;
|
int _SSLConnection;
|
||||||
struct libwebsocket_protocols* _wsProtocols;
|
struct lws_protocols* _wsProtocols;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,7 @@ public: virtual void set##funName(varType var) \
|
||||||
#define CC_BREAK_IF(cond) if(cond) break
|
#define CC_BREAK_IF(cond) if(cond) break
|
||||||
|
|
||||||
#define __CCLOGWITHFUNCTION(s, ...) \
|
#define __CCLOGWITHFUNCTION(s, ...) \
|
||||||
log("%s : %s",__FUNCTION__, StringUtils::format(s, ##__VA_ARGS__).c_str())
|
cocos2d::log("%s : %s",__FUNCTION__, cocos2d::StringUtils::format(s, ##__VA_ARGS__).c_str())
|
||||||
|
|
||||||
/// @name Cocos2d debug
|
/// @name Cocos2d debug
|
||||||
/// @{
|
/// @{
|
||||||
|
|
|
@ -107,8 +107,11 @@ public:
|
||||||
if (data.isBinary)
|
if (data.isBinary)
|
||||||
{// data is binary
|
{// data is binary
|
||||||
JSObject* buffer = JS_NewArrayBuffer(cx, static_cast<uint32_t>(data.len));
|
JSObject* buffer = JS_NewArrayBuffer(cx, static_cast<uint32_t>(data.len));
|
||||||
uint8_t* bufdata = JS_GetArrayBufferData(buffer);
|
if (data.len > 0)
|
||||||
memcpy((void*)bufdata, (void*)data.bytes, data.len);
|
{
|
||||||
|
uint8_t* bufdata = JS_GetArrayBufferData(buffer);
|
||||||
|
memcpy((void*)bufdata, (void*)data.bytes, data.len);
|
||||||
|
}
|
||||||
JS::RootedValue dataVal(cx);
|
JS::RootedValue dataVal(cx);
|
||||||
dataVal = OBJECT_TO_JSVAL(buffer);
|
dataVal = OBJECT_TO_JSVAL(buffer);
|
||||||
JS_SetProperty(cx, jsobj, "data", dataVal);
|
JS_SetProperty(cx, jsobj, "data", dataVal);
|
||||||
|
@ -116,7 +119,20 @@ public:
|
||||||
else
|
else
|
||||||
{// data is string
|
{// data is string
|
||||||
JS::RootedValue dataVal(cx);
|
JS::RootedValue dataVal(cx);
|
||||||
dataVal = c_string_to_jsval(cx, data.bytes);
|
if (strlen(data.bytes) == 0 && data.len > 0)
|
||||||
|
{// String with 0x00 prefix
|
||||||
|
dataVal = STRING_TO_JSVAL(JS_NewStringCopyN(cx, data.bytes, data.len));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{// Normal string
|
||||||
|
dataVal = c_string_to_jsval(cx, data.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataVal.isNullOrUndefined())
|
||||||
|
{
|
||||||
|
ws->closeAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
JS_SetProperty(cx, jsobj, "data", dataVal);
|
JS_SetProperty(cx, jsobj, "data", dataVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +158,10 @@ public:
|
||||||
auto copy = &p->obj;
|
auto copy = &p->obj;
|
||||||
JS::RemoveObjectRoot(cx, copy);
|
JS::RemoveObjectRoot(cx, copy);
|
||||||
jsb_remove_proxy(p);
|
jsb_remove_proxy(p);
|
||||||
|
// Delete WebSocket instance
|
||||||
CC_SAFE_DELETE(ws);
|
CC_SAFE_DELETE(ws);
|
||||||
|
// Delete self at last while websocket was closed.
|
||||||
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onError(WebSocket* ws, const WebSocket::ErrorCode& error)
|
virtual void onError(WebSocket* ws, const WebSocket::ErrorCode& error)
|
||||||
|
@ -180,57 +199,61 @@ void js_cocos2dx_WebSocket_finalize(JSFreeOp *fop, JSObject *obj) {
|
||||||
|
|
||||||
bool js_cocos2dx_extension_WebSocket_send(JSContext *cx, uint32_t argc, jsval *vp)
|
bool js_cocos2dx_extension_WebSocket_send(JSContext *cx, uint32_t argc, jsval *vp)
|
||||||
{
|
{
|
||||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
|
||||||
JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
|
JS::RootedObject obj(cx, argv.thisv().toObjectOrNull());
|
||||||
js_proxy_t *proxy = jsb_get_js_proxy(obj);
|
js_proxy_t *proxy = jsb_get_js_proxy(obj);
|
||||||
WebSocket* cobj = (WebSocket *)(proxy ? proxy->ptr : NULL);
|
WebSocket* cobj = (WebSocket *)(proxy ? proxy->ptr : NULL);
|
||||||
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
||||||
|
|
||||||
if(argc == 1){
|
if(argc == 1)
|
||||||
do
|
{
|
||||||
|
if (argv[0].isString())
|
||||||
{
|
{
|
||||||
if (args.get(0).isString())
|
ssize_t len = JS_GetStringLength(argv[0].toString());
|
||||||
|
std::string data;
|
||||||
|
jsval_to_std_string(cx, argv[0], &data);
|
||||||
|
|
||||||
|
if (data.empty() && len > 0)
|
||||||
{
|
{
|
||||||
std::string data;
|
CCLOGWARN("Text message to send is empty, but its length is greater than 0!");
|
||||||
jsval_to_std_string(cx, args.get(0), &data);
|
//FIXME: Note that this text message contains '0x00' prefix, so its length calcuted by strlen is 0.
|
||||||
cobj->send(data);
|
// we need to fix that if there is '0x00' in text message,
|
||||||
break;
|
// since javascript language could support '0x00' inserted at the beginning or the middle of text message
|
||||||
}
|
|
||||||
|
|
||||||
if (args.get(0).isObject())
|
|
||||||
{
|
|
||||||
uint8_t *bufdata = NULL;
|
|
||||||
uint32_t len = 0;
|
|
||||||
|
|
||||||
JSObject* jsobj = args.get(0).toObjectOrNull();
|
|
||||||
if (JS_IsArrayBufferObject(jsobj))
|
|
||||||
{
|
|
||||||
bufdata = JS_GetArrayBufferData(jsobj);
|
|
||||||
len = JS_GetArrayBufferByteLength(jsobj);
|
|
||||||
}
|
|
||||||
else if (JS_IsArrayBufferViewObject(jsobj))
|
|
||||||
{
|
|
||||||
bufdata = (uint8_t*)JS_GetArrayBufferViewData(jsobj);
|
|
||||||
len = JS_GetArrayBufferViewByteLength(jsobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufdata && len > 0)
|
|
||||||
{
|
|
||||||
cobj->send(bufdata, len);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cobj->send(data);
|
||||||
|
}
|
||||||
|
else if (argv[0].isObject())
|
||||||
|
{
|
||||||
|
uint8_t *bufdata = NULL;
|
||||||
|
uint32_t len = 0;
|
||||||
|
|
||||||
|
JS::RootedObject jsobj(cx, argv[0].toObjectOrNull());
|
||||||
|
if (JS_IsArrayBufferObject(jsobj))
|
||||||
|
{
|
||||||
|
bufdata = JS_GetArrayBufferData(jsobj);
|
||||||
|
len = JS_GetArrayBufferByteLength(jsobj);
|
||||||
|
}
|
||||||
|
else if (JS_IsArrayBufferViewObject(jsobj))
|
||||||
|
{
|
||||||
|
bufdata = (uint8_t*)JS_GetArrayBufferViewData(jsobj);
|
||||||
|
len = JS_GetArrayBufferViewByteLength(jsobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
cobj->send(bufdata, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
JS_ReportError(cx, "data type to be sent is unsupported.");
|
JS_ReportError(cx, "data type to be sent is unsupported.");
|
||||||
|
return false;
|
||||||
} while (0);
|
}
|
||||||
|
|
||||||
|
argv.rval().setUndefined();
|
||||||
|
|
||||||
args.rval().setUndefined();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 0);
|
JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 0);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool js_cocos2dx_extension_WebSocket_close(JSContext *cx, uint32_t argc, jsval *vp){
|
bool js_cocos2dx_extension_WebSocket_close(JSContext *cx, uint32_t argc, jsval *vp){
|
||||||
|
@ -241,7 +264,7 @@ bool js_cocos2dx_extension_WebSocket_close(JSContext *cx, uint32_t argc, jsval *
|
||||||
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
||||||
|
|
||||||
if(argc == 0){
|
if(argc == 0){
|
||||||
cobj->close();
|
cobj->closeAsync();
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,12 @@ var WebSocketTestLayer = cc.Layer.extend({
|
||||||
};
|
};
|
||||||
|
|
||||||
this._wsiSendText.onerror = function(evt) {
|
this._wsiSendText.onerror = function(evt) {
|
||||||
cc.log("sendText Error was fired");
|
cc.log("_wsiSendText Error was fired");
|
||||||
|
if (cc.sys.isObjectValid(self)) {
|
||||||
|
self._errorStatus.setString("an error was fired");
|
||||||
|
} else {
|
||||||
|
cc.log("WebSocket test layer was destroyed!");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._wsiSendText.onclose = function(evt) {
|
this._wsiSendText.onclose = function(evt) {
|
||||||
|
@ -130,55 +135,64 @@ var WebSocketTestLayer = cc.Layer.extend({
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this._wsiSendBinary = new WebSocket("ws://echo.websocket.org");
|
this._wsiSendBinary = new WebSocket("ws://echo.websocket.org");
|
||||||
this._wsiSendBinary.binaryType = "arraybuffer";
|
this._wsiSendBinary.binaryType = "arraybuffer";
|
||||||
this._wsiSendBinary.onopen = function(evt) {
|
this._wsiSendBinary.onopen = function(evt) {
|
||||||
self._sendBinaryStatus.setString("Send Binary WS was opened.");
|
self._sendBinaryStatus.setString("Send Binary WS was opened.");
|
||||||
};
|
};
|
||||||
|
|
||||||
this._wsiSendBinary.onmessage = function(evt) {
|
this._wsiSendBinary.onmessage = function(evt) {
|
||||||
self._sendBinaryTimes++;
|
self._sendBinaryTimes++;
|
||||||
var binary = new Uint16Array(evt.data);
|
var binary = new Uint16Array(evt.data);
|
||||||
var binaryStr = "response bin msg: ";
|
var binaryStr = "response bin msg: ";
|
||||||
|
|
||||||
var str = "";
|
var str = "";
|
||||||
for (var i = 0; i < binary.length; i++) {
|
for (var i = 0; i < binary.length; i++) {
|
||||||
if (binary[i] == 0)
|
if (binary[i] == 0)
|
||||||
{
|
{
|
||||||
str += "\'\\0\'";
|
str += "\'\\0\'";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var hexChar = "0x" + binary[i].toString("16").toUpperCase();
|
var hexChar = "0x" + binary[i].toString("16").toUpperCase();
|
||||||
str += String.fromCharCode(hexChar);
|
str += String.fromCharCode(hexChar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binaryStr += str + ", " + self._sendBinaryTimes;
|
binaryStr += str + ", " + self._sendBinaryTimes;
|
||||||
cc.log(binaryStr);
|
cc.log(binaryStr);
|
||||||
self._sendBinaryStatus.setString(binaryStr);
|
self._sendBinaryStatus.setString(binaryStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
this._wsiSendBinary.onerror = function(evt) {
|
this._wsiSendBinary.onerror = function(evt) {
|
||||||
cc.log("sendBinary Error was fired");
|
cc.log("_wsiSendBinary Error was fired");
|
||||||
};
|
if (cc.sys.isObjectValid(self)) {
|
||||||
|
self._errorStatus.setString("an error was fired");
|
||||||
|
} else {
|
||||||
|
cc.log("WebSocket test layer was destroyed!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this._wsiSendBinary.onclose = function(evt) {
|
this._wsiSendBinary.onclose = function(evt) {
|
||||||
cc.log("_wsiSendBinary websocket instance closed.");
|
cc.log("_wsiSendBinary websocket instance closed.");
|
||||||
self._wsiSendBinary = null;
|
self._wsiSendBinary = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this._wsiError = new WebSocket("ws://invalid.url.com");
|
this._wsiError = new WebSocket("ws://invalidurlxxxyyy.com");
|
||||||
this._wsiError.onopen = function(evt) {};
|
this._wsiError.onopen = function(evt) {};
|
||||||
this._wsiError.onmessage = function(evt) {};
|
this._wsiError.onmessage = function(evt) {};
|
||||||
this._wsiError.onerror = function(evt) {
|
this._wsiError.onerror = function(evt) {
|
||||||
cc.log("Error was fired");
|
cc.log("_wsiError Error was fired");
|
||||||
self._errorStatus.setString("an error was fired");
|
if (cc.sys.isObjectValid(self)) {
|
||||||
};
|
self._errorStatus.setString("an error was fired");
|
||||||
this._wsiError.onclose = function(evt) {
|
} else {
|
||||||
cc.log("_wsiError websocket instance closed.");
|
cc.log("WebSocket test layer was destroyed!");
|
||||||
self._wsiError = null;
|
}
|
||||||
};
|
};
|
||||||
|
this._wsiError.onclose = function(evt) {
|
||||||
|
cc.log("_wsiError websocket instance closed.");
|
||||||
|
self._wsiError = null;
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue