Files
LNXRNT/Sources/websocket_bridge.h

161 lines
4.8 KiB
C
Raw Normal View History

2026-02-20 23:40:15 -08:00
#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);
}