#pragma once #include "Singleton.h" #include "l_squirrel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern HSQUIRRELVM v; extern std::recursive_mutex SqMtx; using namespace asio; using asio::ip::tcp; class Client { private: struct PendingEvent { enum class Type { Connect, Message }; Type type = Type::Message; std::string payload; }; private: tcp::resolver resolver; tcp::socket socket; tcp::resolver::results_type endpoint; std::array buffer; std::array PackData; std::size_t bufferedDataSize = 0; std::deque pendingEvents; std::size_t queuedMessageBytes = 0; std::mutex pendingEventsMutex; std::string Ip = "192.168.200.27"; // std::string Ip = "127.0.0.1"; std::string Port = "65109"; public: bool ConnectState = false; std::fstream file; public: Client(asio::io_context &io_context, std::string Ip, std::string Port) : resolver(io_context), socket(io_context) { endpoint = resolver.resolve(Ip, Port); this->Ip = Ip; this->Port = Port; PackData.fill(0); } private: enum : std::size_t { HeaderSize = 4, MaxQueuedMessages = 10000, MaxQueuedMessageBytes = 8 * 1024 * 1024 }; void ResetPacketBuffer() { bufferedDataSize = 0; } void InvokeGatewayConnectCallback() { std::lock_guard lock(SqMtx); SQInteger top = sq_gettop(v); // saves the stack size before the call sq_pushroottable(v); // pushes the global table sq_pushstring(v, _SC("OnGatewaySocketConnect"), -1); if (SQ_SUCCEEDED(sq_get(v, -2))) { sq_pushroottable(v); // push the global table as this sq_call(v, 1, SQFalse, SQTrue); // calls the function } sq_settop(v, top); // restores the original stack size } void InvokeGatewaySocketMsg(const std::string &message) { std::lock_guard lock(SqMtx); SQInteger top = sq_gettop(v); // saves the stack size before the call sq_pushroottable(v); // pushes the global table sq_pushstring(v, _SC("OnGatewaySocketMsg"), -1); if (SQ_SUCCEEDED(sq_get(v, -2))) { sq_pushroottable(v); sq_pushstring(v, message.c_str(), static_cast(message.size())); sq_call(v, 2, SQFalse, SQTrue); } sq_settop(v, top); } bool EnqueueConnectEvent() { std::lock_guard lock(pendingEventsMutex); PendingEvent event; event.type = PendingEvent::Type::Connect; pendingEvents.push_back(std::move(event)); return true; } bool EnqueueMessageEvent(const char *message, std::size_t length) { std::lock_guard lock(pendingEventsMutex); if (pendingEvents.size() >= MaxQueuedMessages) { spdlog::error("Gateway pending message queue is full. queued_events={}, limit={}", static_cast(pendingEvents.size()), static_cast(MaxQueuedMessages)); return false; } if ((queuedMessageBytes + length) > MaxQueuedMessageBytes) { spdlog::error("Gateway pending message bytes exceeded. queued_bytes={}, incoming={}, limit={}", static_cast(queuedMessageBytes), static_cast(length), static_cast(MaxQueuedMessageBytes)); return false; } PendingEvent event; event.type = PendingEvent::Type::Message; event.payload.assign(message, length); queuedMessageBytes += length; pendingEvents.push_back(std::move(event)); return true; } void DrainPendingEvents(std::deque &events) { std::lock_guard lock(pendingEventsMutex); if (pendingEvents.empty()) return; events.swap(pendingEvents); queuedMessageBytes = 0; } bool AppendReceivedData(std::size_t bytes_transferred) { if (bytes_transferred == 0) return true; if ((bufferedDataSize + bytes_transferred) > PackData.size()) { spdlog::error("Gateway receive buffer overflow. buffered={}, incoming={}, capacity={}", static_cast(bufferedDataSize), static_cast(bytes_transferred), static_cast(PackData.size())); return false; } std::memcpy(PackData.data() + bufferedDataSize, buffer.data(), bytes_transferred); bufferedDataSize += bytes_transferred; return true; } bool ProcessBufferedPackets() { std::size_t offset = 0; while ((bufferedDataSize - offset) >= HeaderSize) { const int packSize = ByteLittleToInt(reinterpret_cast(PackData.data() + offset)); if (packSize <= 0) { spdlog::error("Gateway packet length is invalid: {}", packSize); return false; } const std::size_t payloadSize = static_cast(packSize); if (payloadSize > (PackData.size() - HeaderSize)) { spdlog::error("Gateway packet length exceeds buffer. payload={}, capacity={}", static_cast(payloadSize), static_cast(PackData.size() - HeaderSize)); return false; } if ((bufferedDataSize - offset - HeaderSize) < payloadSize) break; if (!EnqueueMessageEvent(PackData.data() + offset + HeaderSize, payloadSize)) return false; offset += HeaderSize + payloadSize; } if (offset == 0) return true; const std::size_t remaining = bufferedDataSize - offset; if (remaining > 0) std::memmove(PackData.data(), PackData.data() + offset, remaining); bufferedDataSize = remaining; return true; } public: void start() { async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &connected_endpoint) { if (!error) { ConnectState = true; ResetPacketBuffer(); EnqueueConnectEvent(); read(); } else { // std::cerr << "Error connecting to server: " << error.message() << std::endl; start(); } }); } void read() { socket.async_read_some(asio::buffer(buffer), [this](const asio::error_code &error, std::size_t bytes_transferred) { if (!error) { if (!AppendReceivedData(bytes_transferred) || !ProcessBufferedPackets()) { reconnect(); return; } read(); } else { // std::cerr << "Error reading data: " << error.message() << std::endl; usleep(10); reconnect(); } }); } void reconnect() { ConnectState = false; ResetPacketBuffer(); std::cout << "Gateway socket disconnected, reconnecting." << std::endl; asio::error_code close_error; socket.close(close_error); endpoint = resolver.resolve(Ip, Port); async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &connected_endpoint) { if (!error) { ConnectState = true; ResetPacketBuffer(); EnqueueConnectEvent(); read(); } else { reconnect(); } }); } void send(unsigned char *message, int Length) { std::shared_ptr > payload(new std::vector(message, message + Length)); async_write(socket, asio::buffer(*payload), [payload](const asio::error_code &error, std::size_t bytes_transferred) { if (!error) { // std::cout << "Message sent" << std::endl; } else { std::cerr << "Error sending message: " << error.message() << std::endl; } }); } void ClearPack() { ResetPacketBuffer(); } void PackLogic() { std::deque events; DrainPendingEvents(events); while (!events.empty()) { PendingEvent event = std::move(events.front()); events.pop_front(); if (event.type == PendingEvent::Type::Connect) { InvokeGatewayConnectCallback(); continue; } InvokeGatewaySocketMsg(event.payload); } } static int ByteLittleToInt(unsigned char *Count) { int int1 = Count[0] & 0xff; int int2 = (Count[1] & 0xff) << 8; int int3 = (Count[2] & 0xff) << 16; int int4 = (Count[3] & 0xff) << 24; return int1 | int2 | int3 | int4; } }; class l_socket { public: private: l_socket() {} // private constructor to prevent instantiation l_socket(const l_socket &) = delete; // disable copy constructor l_socket &operator=(const l_socket &) = delete; // disable assignment operator Client *ClientObj = nullptr; public: bool InitState = false; static l_socket &getInstance() { static l_socket instance; return instance; } void InitSqr(); void InitPackLogic(); void Init(std::string Ip, std::string Port); void Send(const SQChar *Pck); void Logic(); void IntToByteLittle(unsigned char *b, int Count); public: };