#pragma once #include #include #include #include #include #include #include #include #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 #include #include #include #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 connected{true}; std::atomic handshakeCompleted{false}; std::atomic nonBlockingMode{false}; unsigned remoteAddress, remotePort; std::string handshakeBuffer; // 64KB default with a power of 2 std::unique_ptr recvRingBuffer; std::vector sendQueue; std::atomic queuedBytes{0}; std::atomic sendPending{false}; std::mutex sendQueueMutex; std::unique_ptr recvOverlapped; std::unique_ptr sendOverlapped; // 256KB for high throughput RunTWebSocketClient(int id, kinc_socket_t sock) : clientId(id), socket(sock), remoteAddress(0), remotePort(0), recvRingBuffer(std::make_unique(262144)), recvOverlapped(std::make_unique()), sendOverlapped(std::make_unique()) { 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 isRunning; // lock free no mutex static const size_t MAX_CLIENTS = 1024; std::atomic clients[MAX_CLIENTS]; std::atomic clientCount{0}; std::atomic running; std::thread serverThread; LockFreeBroadcastQueue broadcastQueue; std::thread broadcastWorker; std::atomic 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 createWebSocketFrame(const std::string& data, uint8_t opcode); void sendFrameToClients(const std::vector& frame, int excludeClientId); private: void serverLoop(); void acceptClients(); }; // global server extern std::map 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); }