161 lines
4.8 KiB
C++
161 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include <kinc/network/socket.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <map>
|
|
#include <memory>
|
|
#include "broadcast_queue.h"
|
|
#include "async_engine.h"
|
|
#include "ring_buffer.h"
|
|
|
|
#ifdef _WIN32
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <WinSock2.h>
|
|
#include <WS2tcpip.h>
|
|
#include <mswsock.h>
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
enum class IOCPOperationType {
|
|
IOCP_ACCEPT,
|
|
IOCP_RECV,
|
|
IOCP_SEND
|
|
};
|
|
|
|
// 64KB buffer for recv/send
|
|
struct IOCPOverlapped {
|
|
#ifdef _WIN32
|
|
OVERLAPPED overlapped;
|
|
WSABUF wsaBuf;
|
|
#endif
|
|
IOCPOperationType opType;
|
|
int clientId;
|
|
uint8_t buffer[65536];
|
|
size_t bufferLen;
|
|
};
|
|
|
|
class RunTWebSocketServer;
|
|
struct RunTWebSocketClient;
|
|
|
|
// client with ring buffer
|
|
struct RunTWebSocketClient {
|
|
kinc_socket_t socket;
|
|
int clientId;
|
|
std::atomic<bool> connected{true};
|
|
std::atomic<bool> handshakeCompleted{false};
|
|
std::atomic<bool> nonBlockingMode{false};
|
|
unsigned remoteAddress, remotePort;
|
|
std::string handshakeBuffer;
|
|
|
|
// 64KB default with a power of 2
|
|
std::unique_ptr<RingBuffer> recvRingBuffer;
|
|
|
|
std::vector<uint8_t> sendQueue;
|
|
std::atomic<size_t> queuedBytes{0};
|
|
std::atomic<bool> sendPending{false};
|
|
std::mutex sendQueueMutex;
|
|
|
|
std::unique_ptr<IOCPOverlapped> recvOverlapped;
|
|
std::unique_ptr<IOCPOverlapped> sendOverlapped;
|
|
|
|
// 256KB for high throughput
|
|
RunTWebSocketClient(int id, kinc_socket_t sock)
|
|
: clientId(id), socket(sock), remoteAddress(0), remotePort(0),
|
|
recvRingBuffer(std::make_unique<RingBuffer>(262144)),
|
|
recvOverlapped(std::make_unique<IOCPOverlapped>()),
|
|
sendOverlapped(std::make_unique<IOCPOverlapped>()) {
|
|
recvOverlapped->opType = IOCPOperationType::IOCP_RECV;
|
|
recvOverlapped->clientId = id;
|
|
sendOverlapped->opType = IOCPOperationType::IOCP_SEND;
|
|
sendOverlapped->clientId = id;
|
|
}
|
|
};
|
|
|
|
class RunTWebSocketServer {
|
|
public:
|
|
int serverId;
|
|
std::string host;
|
|
int port;
|
|
int maxConnections;
|
|
kinc_socket_t serverSocket;
|
|
std::atomic<bool> isRunning;
|
|
|
|
// lock free no mutex
|
|
static const size_t MAX_CLIENTS = 1024;
|
|
std::atomic<RunTWebSocketClient*> clients[MAX_CLIENTS];
|
|
std::atomic<size_t> clientCount{0};
|
|
|
|
std::atomic<bool> running;
|
|
std::thread serverThread;
|
|
|
|
LockFreeBroadcastQueue broadcastQueue;
|
|
std::thread broadcastWorker;
|
|
std::atomic<bool> broadcastRunning{false};
|
|
|
|
#ifdef _WIN32
|
|
// IOCP handle for events no polling
|
|
HANDLE iocpHandle = NULL;
|
|
std::thread iocpThread;
|
|
|
|
void iocpEventLoop();
|
|
void postRecv(RunTWebSocketClient* client);
|
|
void postSend(RunTWebSocketClient* client, const uint8_t* data, size_t len);
|
|
void onRecvComplete(RunTWebSocketClient* client, DWORD bytesTransferred);
|
|
void onSendComplete(RunTWebSocketClient* client, DWORD bytesTransferred);
|
|
#endif
|
|
|
|
|
|
RunTWebSocketServer(int id, const std::string& h, int p, int maxConn);
|
|
~RunTWebSocketServer();
|
|
|
|
bool start();
|
|
void stop();
|
|
void tick();
|
|
void sendToAll(const std::string& data);
|
|
void broadcastBinaryFrame(const std::string& binaryData, int excludeClientId);
|
|
void broadcastFrameWithOpcode(const std::string& data, uint8_t opcode, int excludeClientId);
|
|
void broadcastRawFrame(const std::string& rawFrame, int excludeClientId);
|
|
void sendToClient(int clientId, const std::string& data);
|
|
std::string generateWebSocketAccept(const std::string& key);
|
|
void processWebSocketHandshake(RunTWebSocketClient* client);
|
|
void processWebSocketFrames(RunTWebSocketClient* client);
|
|
void processClientData(RunTWebSocketClient* client);
|
|
void broadcastWorkerLoop();
|
|
std::vector<uint8_t> createWebSocketFrame(const std::string& data, uint8_t opcode);
|
|
void sendFrameToClients(const std::vector<uint8_t>& frame, int excludeClientId);
|
|
|
|
private:
|
|
void serverLoop();
|
|
void acceptClients();
|
|
};
|
|
|
|
// global server
|
|
extern std::map<int, RunTWebSocketServer*> g_websocket_servers;
|
|
extern std::mutex g_servers_mutex;
|
|
extern int g_next_server_id;
|
|
|
|
// native bridge functions exposed to V8/JavaScript
|
|
extern "C" {
|
|
int runt_websocket_server_create(const char* host, int port, int maxConnections);
|
|
|
|
bool runt_websocket_server_start(int serverId);
|
|
|
|
void runt_websocket_server_stop(int serverId);
|
|
|
|
void runt_websocket_server_tick(int serverId);
|
|
|
|
void runt_websocket_server_send_all(int serverId, const char* data);
|
|
|
|
void runt_websocket_server_send_all_binary(int serverId, const char* data, size_t length);
|
|
|
|
void runt_websocket_server_send_client(int serverId, int clientId, const char* data);
|
|
|
|
void runt_websocket_server_send_client_binary(int serverId, int clientId, const char* data, size_t length);
|
|
}
|