#pragma once #include "Singleton.h" #include "l_squirrel.h" #include #include #include #include #include extern HSQUIRRELVM v; extern std::recursive_mutex SqMtx; using namespace asio; using asio::ip::tcp; class Client { private: asio::io_context io_context; tcp::resolver resolver; tcp::socket socket; tcp::resolver::results_type endpoint; std::array buffer; std::array PackData; std::size_t writeDataSize = 0; std::size_t receivedDataSize = 0; std::string Ip = "192.168.200.27"; // std::string Ip = "127.0.0.1"; std::string Port = "65109"; std::mutex SizeMtx; std::mutex ClearFlagMtx; public: bool ConnectState = false; bool ClearFlag = false; int LogiThreadSize = 0; 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); } void start() { async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &endpoint) { if (!error) { ConnectState = true; read(); 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))) { // gets the field 'foo' from the global table sq_pushroottable(v); // push the 'this' (in this case is the global table) sq_call(v, 1, SQFalse, SQTrue); // calls the function } sq_settop(v, top); // restores the original stack size io_context.poll(); // 处理一次事件循环,避免主线程阻塞 } 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 (ClearFlag) ClearPack(); for (std::size_t i = 0; i < bytes_transferred; ++i) { PackData[writeDataSize + i] = buffer[i]; } SizeMtx.lock(); writeDataSize += bytes_transferred; SizeMtx.unlock(); read(); } else { std::cerr << "Error reading data: " << error.message() << std::endl; reconnect(); } }); } void reconnect() { ConnectState = false; std::cout << "服务器断开连接,尝试重连." << std::endl; // 执行重连操作 // 关闭当前连接 socket.close(); // 重新解析端点 endpoint = resolver.resolve(Ip, Port); // 重新连接 async_connect(socket, endpoint, [this](const asio::error_code &error, const tcp::endpoint &endpoint) { if (!error) { ConnectState = true; 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))) { // gets the field 'foo' from the global table sq_pushroottable(v); // push the 'this' (in this case is the global table) sq_call(v, 1, SQFalse, SQTrue); // calls the function } sq_settop(v, top); // restores the original stack size read(); } else { std::cerr << "Error reconnecting to server: " << error.message() << std::endl; // 可以在此处添加重连失败的处理逻辑 reconnect(); } }); } void send(unsigned char *message, int Length) { async_write(socket, asio::buffer(message, Length), [this](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() { SizeMtx.lock(); // 复制已读大小开始到结束到 开始 作用删除前已读大小的数据 std::copy(PackData.begin() + receivedDataSize, PackData.end(), PackData.begin()); // 将最后元素设置为0 std::fill(PackData.end() - receivedDataSize, PackData.end(), 0); // 写的大小重置 writeDataSize -= receivedDataSize; // 读的大小重置 receivedDataSize = 0; SizeMtx.unlock(); ClearFlagMtx.lock(); ClearFlag = false; ClearFlagMtx.unlock(); } void PackLogic() { int RealSize = 0; int RealReadSize = -1; if (SizeMtx.try_lock()) { receivedDataSize += LogiThreadSize; LogiThreadSize = 0; RealSize = writeDataSize; RealReadSize = receivedDataSize; SizeMtx.unlock(); } // 如果包长度不够直接返回 if (RealSize < 4 || RealReadSize == -1) return; // 如果里面已经读了超过一半的大小了 if (RealSize >= 40960) { if (!ClearFlag) { ClearFlagMtx.lock(); ClearFlag = true; ClearFlagMtx.unlock(); } } unsigned char *Count = new unsigned char[4]; std::copy(PackData.begin() + RealReadSize, PackData.begin() + 4 + RealReadSize, Count); int PackSize = ByteLittleToInt(Count); delete[] Count; // 如果包长度不够或者缓冲区长度直接返回 if (PackSize <= 0 || ((RealSize - RealReadSize - 4) < PackSize)) return; char *StrBuffer = new char[PackSize]; std::copy(PackData.begin() + 4 + RealReadSize, PackData.begin() + 4 + PackSize + RealReadSize, StrBuffer); std::string Str(StrBuffer, PackSize); delete[] StrBuffer; // 包数据大小读取的偏移 这次读了多少 LogiThreadSize += (4 + PackSize); // std::cout << "包大小: " << PackSize << std::endl; // std::cout << "包内容: " << Str << std::endl; // std::cout << "收到了第: " << TestCode << "个包" << std::endl; // spdlog::info(Str); 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))) { // gets the field 'foo' from the global table sq_pushroottable(v); // push the 'this' (in this case is the global table) sq_pushstring(v, Str.c_str(), PackSize); sq_call(v, 2, SQFalse, SQTrue); // calls the function } sq_settop(v, top); // restores the original stack size // TestCode++; } 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; 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: };