cocos-enhance-kit/cocos2d-x/cocos/network/WebSocketServer.h
2022-06-25 00:23:03 +08:00

320 lines
10 KiB
C++

/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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.
****************************************************************************/
#pragma once
#include "base/ccConfig.h"
#if (USE_SOCKET > 0) && (USE_WEBSOCKET_SERVER > 0)
#include "uv.h"
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <memory>
#include <map>
#include <list>
#include <unordered_map>
#include <thread>
#include <mutex>
#include <atomic>
#include "platform/CCPlatformDefine.h"
#include "websockets/libwebsockets.h"
namespace cocos2d {
namespace network {
class WebSocketServer;
class WebSocketServerConnection;
/**
* receive/send data buffer with reserved bytes
*/
class DataFrame {
public:
DataFrame(const std::string& data);
DataFrame(const void* data, int len, bool isBinary = true);
virtual ~DataFrame();
void append(unsigned char* p, int len);
int slice(unsigned char** p, int len);
int consume(int len);
int remain() const;
inline bool isBinary() const { return _isBinary; }
inline bool isString() const { return !_isBinary; }
inline bool isFront() const { return _consumed == 0; }
void setCallback(std::function<void(const std::string&)> callback)
{
_callback = callback;
}
void onFinish(const std::string& message) {
if (_callback) {
_callback(message);
}
}
inline int size() const { return _underlyingData.size() - LWS_PRE; }
std::string toString();
unsigned char* getData() { return _underlyingData.data() + LWS_PRE; }
private:
std::vector<unsigned char> _underlyingData;
int _consumed = 0;
bool _isBinary = false;
std::function<void(const std::string&) > _callback;
};
class CC_DLL WebSocketServerConnection {
public:
WebSocketServerConnection(struct lws* wsi);
virtual ~WebSocketServerConnection();
enum ReadyState {
CONNECTING = 1,
OPEN = 2,
CLOSING = 3,
CLOSED = 4
};
void sendTextAsync(const std::string&, std::function<void(const std::string&)> callback);
void sendBinaryAsync(const void*, size_t len, std::function<void(const std::string&)> callback);
void closeAsync(int code, std::string reasson);
/** stream is not implemented*/
//bool beginBinary();
/** should implement in JS */
// bool send();
// bool sendPing(std::string)
//int getSocket();
//std::shared_ptr<WebSocketServer>& getServer();
//std::string& getPath();
inline int getReadyState() const {
return (int)_readyState;
}
std::map<std::string, std::string> getHeaders();
std::vector<std::string> getProtocols();
inline void setOnClose(std::function<void(int, const std::string&)> cb)
{
_onclose = cb;
}
inline void setOnError(std::function<void(const std::string&)> cb)
{
_onerror = cb;
}
inline void setOnText(std::function<void(std::shared_ptr<DataFrame>)> cb)
{
_ontext = cb;
}
inline void setOnBinary(std::function<void(std::shared_ptr<DataFrame>)> cb)
{
_onbinary = cb;
}
inline void setOnData(std::function<void(std::shared_ptr<DataFrame>)> cb)
{
_ondata = cb;
}
inline void setOnConnect(std::function<void()> cb)
{
_onconnect = cb;
}
inline void setOnEnd(std::function<void()> cb)
{
_onend = cb;
}
void onClientCloseInit();
inline void setData(void* d) { _data = d; }
inline void* getData() const { return _data; }
private:
bool send(std::shared_ptr<DataFrame> data);
bool close(int code, std::string reasson);
inline void scheduleSend() {
if (_wsi)
lws_callback_on_writable(_wsi);
}
void onConnected();
void onDataReceive(void* in, int len);
int onDrainData();
void onHTTP();
void onClientCloseInit(int code, const std::string& msg);
void onDestroyClient();
struct lws* _wsi = nullptr;
std::map<std::string, std::string> _headers;
std::list<std::shared_ptr<DataFrame>> _sendQueue;
std::shared_ptr<DataFrame> _prevPkg;
bool _closed = false;
std::string _closeReason = "close connection";
int _closeCode = 1000;
std::atomic<ReadyState> _readyState{ReadyState::CLOSED};
// Attention: do not reference **this** in callbacks
std::function<void(int, const std::string&)> _onclose;
std::function<void(const std::string&)> _onerror;
std::function<void(std::shared_ptr<DataFrame>)> _ontext;
std::function<void(std::shared_ptr<DataFrame>)> _onbinary;
std::function<void(std::shared_ptr<DataFrame>)> _ondata;
std::function<void()> _onconnect;
std::function<void()> _onend;
uv_async_t _async = {0};
void* _data = nullptr;
friend class WebSocketServer;
};
class CC_DLL WebSocketServer {
public:
WebSocketServer();
virtual ~WebSocketServer();
static void listenAsync(std::shared_ptr<WebSocketServer>& server, int port, const std::string& host, std::function<void(const std::string & errorMsg)> callback);
void closeAsync(std::function<void(const std::string & errorMsg)> callback = nullptr);
std::vector<std::shared_ptr<WebSocketServerConnection>> getConnections() const;
void setOnListening(std::function<void(const std::string&)> cb)
{
_onlistening = cb;
}
void setOnError(std::function<void(const std::string&)> cb)
{
_onerror = cb;
}
void setOnClose(std::function<void(const std::string&)> cb)
{
_onclose = cb;
}
void setOnConnection(std::function<void(std::shared_ptr<WebSocketServerConnection>)> cb)
{
_onconnection = cb;
}
inline void setOnEnd(std::function<void()> cb)
{
_onend = cb;
}
inline void setOnBegin(std::function<void()> cb)
{
_onbegin = cb;
}
inline void setData(void* d) { _data = d; }
inline void* getData() const { return _data; }
protected:
static void listen(std::shared_ptr<WebSocketServer> server, int port, const std::string& host, std::function<void(const std::string & errorMsg)> callback);
bool close(std::function<void(const std::string & errorMsg)> callback = nullptr);
void onCreateClient(struct lws* wsi);
void onDestroyClient(struct lws* wsi);
void onCloseClient(struct lws* wsi);
void onCloseClientInit(struct lws* wsi, void* in, int len);
void onClientReceive(struct lws* wsi, void* in, int len);
int onServerWritable(struct lws* wsi);
void onClientHTTP(struct lws* wsi);
private:
std::shared_ptr<WebSocketServerConnection> findConnection(struct lws *wsi);
void destroyContext();
std::string _host;
lws_context* _ctx = nullptr;
uv_async_t _async = {0};
mutable std::mutex _connsMtx;
std::unordered_map<struct lws*, std::shared_ptr<WebSocketServerConnection> > _conns;
// Attention: do not reference **this** in callbacks
std::function<void(const std::string&)> _onlistening;
std::function<void(const std::string&)> _onerror;
std::function<void(const std::string&)> _onclose;
std::function<void(const std::string&)> _onclose_cb;
std::function<void()> _onend;
std::function<void()> _onbegin;
std::function<void(std::shared_ptr<WebSocketServerConnection>)> _onconnection;
enum class ServerThreadState{
NOT_BOOTED,
ST_ERROR,
RUNNING,
STOPPED,
};
std::atomic<ServerThreadState> _serverState {ServerThreadState::NOT_BOOTED};
std::mutex _serverLock;
void* _data = nullptr;
public:
static int _websocketServerCallback(struct lws* wsi, enum lws_callback_reasons reason,
void* user, void* in, size_t len);
};
}
}
#endif //#if (USE_SOCKET > 0) && (USE_WEBSOCKET_SERVER > 0)