Files
dps_lib/include/l_socket.h
Lenheart a9af16962c feat(网络): 重构网关socket连接与消息处理逻辑
- 重构Client类,使用事件队列管理连接和消息事件
- 增加消息队列大小限制和错误处理
- 优化数据包处理流程,减少内存拷贝
- 添加nullptr检查防止空指针访问
- 更新版本号至1.4
- 新增TimerQueue_GetTimerMess函数声明和hook
2026-05-08 17:28:59 +08:00

341 lines
10 KiB
C++

#pragma once
#include "Singleton.h"
#include "l_squirrel.h"
#include <array>
#include <asio.hpp>
#include <cstring>
#include <deque>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <string>
#include <unistd.h>
#include <vector>
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<char, 8192> buffer;
std::array<char, 8192000> PackData;
std::size_t bufferedDataSize = 0;
std::deque<PendingEvent> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<SQInteger>(message.size()));
sq_call(v, 2, SQFalse, SQTrue);
}
sq_settop(v, top);
}
bool EnqueueConnectEvent()
{
std::lock_guard<std::mutex> 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<std::mutex> lock(pendingEventsMutex);
if (pendingEvents.size() >= MaxQueuedMessages)
{
spdlog::error("Gateway pending message queue is full. queued_events={}, limit={}",
static_cast<unsigned long long>(pendingEvents.size()),
static_cast<unsigned long long>(MaxQueuedMessages));
return false;
}
if ((queuedMessageBytes + length) > MaxQueuedMessageBytes)
{
spdlog::error("Gateway pending message bytes exceeded. queued_bytes={}, incoming={}, limit={}",
static_cast<unsigned long long>(queuedMessageBytes),
static_cast<unsigned long long>(length),
static_cast<unsigned long long>(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<PendingEvent> &events)
{
std::lock_guard<std::mutex> 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<unsigned long long>(bufferedDataSize),
static_cast<unsigned long long>(bytes_transferred),
static_cast<unsigned long long>(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<unsigned char *>(PackData.data() + offset));
if (packSize <= 0)
{
spdlog::error("Gateway packet length is invalid: {}", packSize);
return false;
}
const std::size_t payloadSize = static_cast<std::size_t>(packSize);
if (payloadSize > (PackData.size() - HeaderSize))
{
spdlog::error("Gateway packet length exceeds buffer. payload={}, capacity={}",
static_cast<unsigned long long>(payloadSize),
static_cast<unsigned long long>(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<std::vector<unsigned char> > payload(new std::vector<unsigned char>(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<PendingEvent> 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:
};