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/CCAsyncTaskPool.h"
|
||||
#include "platform/CCApplication.h"
|
||||
#include "network/WebSocket.h"
|
||||
|
||||
#if CC_ENABLE_SCRIPT_BINDING
|
||||
#include "CCScriptSupport.h"
|
||||
|
@ -930,6 +931,9 @@ void Director::reset()
|
|||
_runningScene = nullptr;
|
||||
_nextScene = nullptr;
|
||||
|
||||
// Close all websocket connection. It has to be invoked before cleaning scheduler
|
||||
network::WebSocket::closeAllConnections();
|
||||
|
||||
// cleanup scheduler
|
||||
getScheduler()->unscheduleAll();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,13 +32,15 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory> // for std::shared_ptr
|
||||
|
||||
#include "platform/CCPlatformMacros.h"
|
||||
#include "platform/CCStdC.h"
|
||||
|
||||
struct libwebsocket;
|
||||
struct libwebsocket_context;
|
||||
struct libwebsocket_protocols;
|
||||
struct lws;
|
||||
struct lws_context;
|
||||
struct lws_protocols;
|
||||
|
||||
/**
|
||||
* @addtogroup network
|
||||
|
@ -50,14 +52,20 @@ NS_CC_BEGIN
|
|||
namespace network {
|
||||
|
||||
class WsThreadHelper;
|
||||
class WsMessage;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
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.
|
||||
*
|
||||
|
@ -77,10 +85,11 @@ public:
|
|||
*/
|
||||
struct Data
|
||||
{
|
||||
Data():bytes(nullptr), len(0), issued(0), isBinary(false){}
|
||||
Data():bytes(nullptr), len(0), issued(0), isBinary(false), ext(nullptr){}
|
||||
char* bytes;
|
||||
ssize_t len, issued;
|
||||
bool isBinary;
|
||||
void* ext;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,9 +188,18 @@ public:
|
|||
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();
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -190,36 +208,38 @@ public:
|
|||
State getReadyState();
|
||||
|
||||
private:
|
||||
virtual void onSubThreadStarted();
|
||||
virtual int onSubThreadLoop();
|
||||
virtual void onSubThreadEnded();
|
||||
virtual void onUIThreadReceiveMessage(WsMessage* msg);
|
||||
void onSubThreadStarted();
|
||||
void onSubThreadLoop();
|
||||
void onSubThreadEnded();
|
||||
|
||||
// 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;
|
||||
int onSocketCallback(struct libwebsocket_context *ctx,
|
||||
struct libwebsocket *wsi,
|
||||
int reason,
|
||||
void *user, void *in, ssize_t len);
|
||||
void onClientWritable();
|
||||
void onClientReceivedData(void* in, ssize_t len);
|
||||
void onConnectionOpened();
|
||||
void onConnectionError();
|
||||
void onConnectionClosed();
|
||||
|
||||
private:
|
||||
std::mutex _readStateMutex;
|
||||
State _readyState;
|
||||
std::string _host;
|
||||
unsigned int _port;
|
||||
std::string _path;
|
||||
|
||||
ssize_t _pendingFrameDataLen;
|
||||
ssize_t _currentDataLen;
|
||||
char *_currentData;
|
||||
std::vector<char> _receivedData;
|
||||
|
||||
friend class WsThreadHelper;
|
||||
friend class WebSocketCallbackWrapper;
|
||||
WsThreadHelper* _wsHelper;
|
||||
|
||||
struct libwebsocket* _wsInstance;
|
||||
struct libwebsocket_context* _wsContext;
|
||||
struct lws* _wsInstance;
|
||||
struct lws_context* _wsContext;
|
||||
std::shared_ptr<bool> _isDestroyed;
|
||||
Delegate* _delegate;
|
||||
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 __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
|
||||
/// @{
|
||||
|
|
|
@ -107,8 +107,11 @@ public:
|
|||
if (data.isBinary)
|
||||
{// data is binary
|
||||
JSObject* buffer = JS_NewArrayBuffer(cx, static_cast<uint32_t>(data.len));
|
||||
uint8_t* bufdata = JS_GetArrayBufferData(buffer);
|
||||
memcpy((void*)bufdata, (void*)data.bytes, data.len);
|
||||
if (data.len > 0)
|
||||
{
|
||||
uint8_t* bufdata = JS_GetArrayBufferData(buffer);
|
||||
memcpy((void*)bufdata, (void*)data.bytes, data.len);
|
||||
}
|
||||
JS::RootedValue dataVal(cx);
|
||||
dataVal = OBJECT_TO_JSVAL(buffer);
|
||||
JS_SetProperty(cx, jsobj, "data", dataVal);
|
||||
|
@ -116,7 +119,20 @@ public:
|
|||
else
|
||||
{// data is string
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -142,7 +158,10 @@ public:
|
|||
auto copy = &p->obj;
|
||||
JS::RemoveObjectRoot(cx, copy);
|
||||
jsb_remove_proxy(p);
|
||||
// Delete WebSocket instance
|
||||
CC_SAFE_DELETE(ws);
|
||||
// Delete self at last while websocket was closed.
|
||||
delete this;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
|
||||
JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
|
||||
JS::RootedObject obj(cx, argv.thisv().toObjectOrNull());
|
||||
js_proxy_t *proxy = jsb_get_js_proxy(obj);
|
||||
WebSocket* cobj = (WebSocket *)(proxy ? proxy->ptr : NULL);
|
||||
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
||||
|
||||
if(argc == 1){
|
||||
do
|
||||
if(argc == 1)
|
||||
{
|
||||
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;
|
||||
jsval_to_std_string(cx, args.get(0), &data);
|
||||
cobj->send(data);
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
CCLOGWARN("Text message to send is empty, but its length is greater than 0!");
|
||||
//FIXME: Note that this text message contains '0x00' prefix, so its length calcuted by strlen is 0.
|
||||
// we need to fix that if there is '0x00' in text message,
|
||||
// since javascript language could support '0x00' inserted at the beginning or the middle of text message
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
} while (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
argv.rval().setUndefined();
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
||||
return true;
|
||||
}
|
||||
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){
|
||||
|
@ -241,7 +264,7 @@ bool js_cocos2dx_extension_WebSocket_close(JSContext *cx, uint32_t argc, jsval *
|
|||
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
|
||||
|
||||
if(argc == 0){
|
||||
cobj->close();
|
||||
cobj->closeAsync();
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,12 @@ var WebSocketTestLayer = cc.Layer.extend({
|
|||
};
|
||||
|
||||
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) {
|
||||
|
@ -130,55 +135,64 @@ var WebSocketTestLayer = cc.Layer.extend({
|
|||
};
|
||||
|
||||
|
||||
this._wsiSendBinary = new WebSocket("ws://echo.websocket.org");
|
||||
this._wsiSendBinary.binaryType = "arraybuffer";
|
||||
this._wsiSendBinary.onopen = function(evt) {
|
||||
self._sendBinaryStatus.setString("Send Binary WS was opened.");
|
||||
};
|
||||
this._wsiSendBinary = new WebSocket("ws://echo.websocket.org");
|
||||
this._wsiSendBinary.binaryType = "arraybuffer";
|
||||
this._wsiSendBinary.onopen = function(evt) {
|
||||
self._sendBinaryStatus.setString("Send Binary WS was opened.");
|
||||
};
|
||||
|
||||
this._wsiSendBinary.onmessage = function(evt) {
|
||||
self._sendBinaryTimes++;
|
||||
var binary = new Uint16Array(evt.data);
|
||||
var binaryStr = "response bin msg: ";
|
||||
this._wsiSendBinary.onmessage = function(evt) {
|
||||
self._sendBinaryTimes++;
|
||||
var binary = new Uint16Array(evt.data);
|
||||
var binaryStr = "response bin msg: ";
|
||||
|
||||
var str = "";
|
||||
for (var i = 0; i < binary.length; i++) {
|
||||
if (binary[i] == 0)
|
||||
{
|
||||
str += "\'\\0\'";
|
||||
}
|
||||
else
|
||||
{
|
||||
var hexChar = "0x" + binary[i].toString("16").toUpperCase();
|
||||
str += String.fromCharCode(hexChar);
|
||||
}
|
||||
}
|
||||
var str = "";
|
||||
for (var i = 0; i < binary.length; i++) {
|
||||
if (binary[i] == 0)
|
||||
{
|
||||
str += "\'\\0\'";
|
||||
}
|
||||
else
|
||||
{
|
||||
var hexChar = "0x" + binary[i].toString("16").toUpperCase();
|
||||
str += String.fromCharCode(hexChar);
|
||||
}
|
||||
}
|
||||
|
||||
binaryStr += str + ", " + self._sendBinaryTimes;
|
||||
cc.log(binaryStr);
|
||||
self._sendBinaryStatus.setString(binaryStr);
|
||||
};
|
||||
binaryStr += str + ", " + self._sendBinaryTimes;
|
||||
cc.log(binaryStr);
|
||||
self._sendBinaryStatus.setString(binaryStr);
|
||||
};
|
||||
|
||||
this._wsiSendBinary.onerror = function(evt) {
|
||||
cc.log("sendBinary Error was fired");
|
||||
};
|
||||
this._wsiSendBinary.onerror = function(evt) {
|
||||
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) {
|
||||
cc.log("_wsiSendBinary websocket instance closed.");
|
||||
self._wsiSendBinary = null;
|
||||
};
|
||||
this._wsiSendBinary.onclose = function(evt) {
|
||||
cc.log("_wsiSendBinary websocket instance closed.");
|
||||
self._wsiSendBinary = null;
|
||||
};
|
||||
|
||||
this._wsiError = new WebSocket("ws://invalid.url.com");
|
||||
this._wsiError.onopen = function(evt) {};
|
||||
this._wsiError.onmessage = function(evt) {};
|
||||
this._wsiError.onerror = function(evt) {
|
||||
cc.log("Error was fired");
|
||||
self._errorStatus.setString("an error was fired");
|
||||
};
|
||||
this._wsiError.onclose = function(evt) {
|
||||
cc.log("_wsiError websocket instance closed.");
|
||||
self._wsiError = null;
|
||||
};
|
||||
this._wsiError = new WebSocket("ws://invalidurlxxxyyy.com");
|
||||
this._wsiError.onopen = function(evt) {};
|
||||
this._wsiError.onmessage = function(evt) {};
|
||||
this._wsiError.onerror = function(evt) {
|
||||
cc.log("_wsiError Error was fired");
|
||||
if (cc.sys.isObjectValid(self)) {
|
||||
self._errorStatus.setString("an error was fired");
|
||||
} else {
|
||||
cc.log("WebSocket test layer was destroyed!");
|
||||
}
|
||||
};
|
||||
this._wsiError.onclose = function(evt) {
|
||||
cc.log("_wsiError websocket instance closed.");
|
||||
self._wsiError = null;
|
||||
};
|
||||
|
||||
return true;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue