This commit is contained in:
2026-04-22 19:36:06 +08:00
commit bb062ecfb4
370 changed files with 291868 additions and 0 deletions

547
include/SqrReg_Dio.hpp Normal file
View File

@@ -0,0 +1,547 @@
#pragma once
#include "squirrel.h"
#include "sqstdaux.h"
#include "sqstdblob.h"
#include "sqstdio.h"
#include "sqstdmath.h"
#include "sqstdstring.h"
#include "sqstdsystem.h"
#include <asio.hpp>
#include <asio/ssl.hpp>
#include <asio/basic_socket_streambuf.hpp>
#include <iostream>
#include <thread>
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <memory> // 新增:用于 std::unique_ptr
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <stdint.h>
// 全局变量声明需在cpp文件中定义
extern HSQUIRRELVM v;
extern std::recursive_mutex SqMtx;
using tcp = asio::ip::tcp;
// ====================== 工具函数 ======================
// 字符串转小写
std::string to_lower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::tolower(c); });
return s;
}
// 修剪字符串两端空格
std::string trim(std::string s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](unsigned char c) { return !std::isspace(c); }));
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char c) { return !std::isspace(c); }).base(), s.end());
return s;
}
// 解析 URL分离路径和查询参数
void parse_url(const std::string& url, std::string& path, std::string& query) {
size_t query_pos = url.find('?');
if (query_pos == std::string::npos) {
path = url;
query = "";
}
else {
path = url.substr(0, query_pos);
query = url.substr(query_pos + 1);
}
}
// ====================== HTTP 头解析结构体 ======================
struct HttpRequestHeader {
std::string method; // GET/POST/PUT/DELETE 等
std::string path; // 请求路径(如 /api/test
std::string version; // HTTP/1.1 / HTTP/1.0
std::string query_string; // URL 查询参数(如 ?id=1&name=test
// 头部键值对(统一转小写,避免大小写问题)
std::unordered_map<std::string, std::string> headers;
// 便捷获取头部值(兼容大小写)
std::string get_header(const std::string& key) const {
std::string lower_key = to_lower(key);
auto it = headers.find(lower_key);
return it != headers.end() ? it->second : "";
}
};
// ====================== HTTP 头解析函数 ======================
HttpRequestHeader parse_http_header(const std::string& raw_header) {
HttpRequestHeader result;
std::istringstream iss(raw_header);
std::string line;
bool first_line_parsed = false;
// 第一步:解析请求首行(仅解析第一行,避免重复)
while (std::getline(iss, line)) {
// 移除行尾的 \r
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
line = trim(line);
// 跳过空行
if (line.empty()) {
continue;
}
// 仅解析第一个非空行作为请求首行
if (!first_line_parsed) {
std::string url;
std::istringstream line_iss(line);
line_iss >> result.method >> url >> result.version;
parse_url(url, result.path, result.query_string);
std::transform(result.method.begin(), result.method.end(), result.method.begin(),
[](unsigned char c) { return std::toupper(c); });
first_line_parsed = true;
continue;
}
// 解析头部键值对
size_t colon_pos = line.find(':');
if (colon_pos == std::string::npos) {
continue;
}
std::string key = trim(line.substr(0, colon_pos));
std::string value = trim(line.substr(colon_pos + 1));
result.headers[to_lower(key)] = value;
}
return result;
}
// ====================== 读取完整原始请求 ======================
std::string read_full_raw_request(tcp::socket& socket, HttpRequestHeader& header_out) {
asio::streambuf buf;
std::string full_request;
asio::error_code ec;
try {
// 第一步:读取头部(直到 \r\n\r\n
size_t header_bytes = asio::read_until(socket, buf, "\r\n\r\n", ec);
if (ec) {
throw asio::system_error(ec);
}
// 提取头部内容
std::string header_str(asio::buffer_cast<const char*>(buf.data()), header_bytes);
full_request = header_str;
// 安全清理头部末尾的 \r\n\r\n核心修复先判断是否找到
size_t end_header_pos = header_str.find("\r\n\r\n"); // 用 find 而非 find_last_of精准匹配结束符
if (end_header_pos != std::string::npos) {
// 只保留头部内容,截断后面的空行
header_str = header_str.substr(0, end_header_pos);
}
// 解析头部
header_out = parse_http_header(header_str);
// 第二步:读取请求体(严格按 Content-Length 读取)
size_t content_len = 0;
std::string cl_str = header_out.get_header("Content-Length");
if (!cl_str.empty()) {
try {
content_len = std::stoul(cl_str);
}
catch (...) {
content_len = 0;
}
}
// 清空缓冲区中已读取的头部数据
buf.consume(header_bytes);
// 读取请求体(防越界)
if (content_len > 0) {
// 先检查剩余缓冲区是否足够,避免读取超限
size_t buf_available = buf.size();
if (buf_available < content_len) {
// 读取剩余需要的字节
asio::read(socket, buf, asio::transfer_exactly(content_len - buf_available), ec);
if (ec && ec != asio::error::eof) {
throw asio::system_error(ec);
}
}
// 提取请求体(防缓冲区数据不足)
size_t actual_read = std::min(buf.size(), content_len);
std::string body_str(asio::buffer_cast<const char*>(buf.data()), actual_read);
full_request += body_str;
// 清理缓冲区
buf.consume(actual_read);
}
}
catch (...) {
throw;
}
return full_request;
}
// ====================== 将 HttpRequestHeader 转为 Squirrel Table ======================
void push_http_header_to_squirrel(HSQUIRRELVM v, const HttpRequestHeader& header) {
// 1. 创建空 Table
sq_newtable(v);
// 2. 设置基础字段method/path/version/query_string
// method
sq_pushstring(v, _SC("method"), -1);
sq_pushstring(v, header.method.c_str(), -1);
sq_rawset(v, -3);
// path
sq_pushstring(v, _SC("path"), -1);
sq_pushstring(v, header.path.c_str(), -1);
sq_rawset(v, -3);
// version
sq_pushstring(v, _SC("version"), -1);
sq_pushstring(v, header.version.c_str(), -1);
sq_rawset(v, -3);
// query_string
sq_pushstring(v, _SC("query_string"), -1);
sq_pushstring(v, header.query_string.c_str(), -1);
sq_rawset(v, -3);
// 3. 创建 headers 子 Table
sq_pushstring(v, _SC("headers"), -1);
sq_newtable(v);
// 遍历头部键值对,写入子 Table
for (const auto& pair : header.headers) {
sq_pushstring(v, pair.first.c_str(), -1); // 键(小写)
sq_pushstring(v, pair.second.c_str(), -1); // 值
sq_rawset(v, -3);
}
// 将 headers 子 Table 写入主 Table
sq_rawset(v, -3);
}
// ====================== 处理客户端请求 ======================
void handle_client(tcp::socket* socket, HSQOBJECT HttpServerObject) {
if (!socket || !socket->is_open()) {
std::cerr << "无效的 socket 连接" << std::endl;
return;
}
try {
// 读取完整请求并解析头部
HttpRequestHeader header;
std::string full_request = read_full_raw_request(*socket, header);
// 加锁操作 Squirrel VMlock_guard 自动管理锁生命周期)
std::lock_guard<std::recursive_mutex> lock(SqMtx);
// 执行 Squirrel 的 Event 函数
SQInteger top = sq_gettop(v); // 保存栈状态
sq_pushobject(v, HttpServerObject);
sq_pushstring(v, _SC("Event"), -1);
if (SQ_SUCCEEDED(sq_get(v, -2))) {
// 压入函数参数:
// 参数1HttpServerObjectthis
sq_pushobject(v, HttpServerObject);
// 参数2socket 指针userpointer
sq_pushuserpointer(v, socket);
// 参数3解析后的 header table
push_http_header_to_squirrel(v, header);
// 参数4完整原始请求字符串
size_t body_start_pos = full_request.find("\r\n\r\n");
std::string request_body = (body_start_pos != std::string::npos) ? full_request.substr(body_start_pos + 4) : "";
sq_pushstring(v, request_body.c_str(), request_body.size());
// 调用函数:参数数量=4无返回值开启错误捕获
sq_call(v, 4, SQFalse, SQTrue);
}
sq_settop(v, top); // 恢复栈状态
}
catch (const asio::system_error& e) {
std::cerr << "网络错误: " << e.what() << " (错误码: " << e.code() << ")" << std::endl;
}
catch (const std::exception& e) {
std::cerr << "处理请求错误: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "未知错误" << std::endl;
}
}
// ====================== 服务器上下文(支持多实例) ======================
struct HttpServerCtx {
uint64_t server_id; // 唯一服务器ID多实例核心
std::atomic<bool> running{ true }; // 退出标志atomic不可拷贝
HSQUIRRELVM vm; // Squirrel VM
HSQOBJECT http_server_obj; // 保存的对象
asio::io_context* io_context = nullptr; // IO上下文用于停止
std::string host; // 记录主机(调试用)
std::string port; // 记录端口(调试用)
// 禁用拷贝构造和赋值(显式声明,避免误拷贝)
HttpServerCtx(const HttpServerCtx&) = delete;
HttpServerCtx& operator=(const HttpServerCtx&) = delete;
// 允许移动构造用于unique_ptr
HttpServerCtx(HttpServerCtx&&) = default;
HttpServerCtx& operator=(HttpServerCtx&&) = default;
// 构造函数
HttpServerCtx() = default;
HttpServerCtx(uint64_t id, HSQUIRRELVM vm_ptr, HSQOBJECT obj, const std::string& h, const std::string& p)
: server_id(id), vm(vm_ptr), http_server_obj(obj), host(h), port(p) {
}
};
// ====================== 全局服务器管理核心修复用unique_ptr存储避免拷贝 ======================
static std::unordered_map<uint64_t, std::unique_ptr<HttpServerCtx>> g_server_ctxs;
static std::atomic<uint64_t> g_next_server_id{ 1 }; // 自增唯一IDatomic不可拷贝直接用
static std::mutex g_server_mutex; // 保护全局容器
// ====================== 服务器主函数 ======================
void start_server(uint64_t server_id, const std::string& host, const std::string& port) {
// 查找上下文(加锁)
std::unique_lock<std::mutex> lock(g_server_mutex);
auto it = g_server_ctxs.find(server_id);
if (it == g_server_ctxs.end()) {
std::cerr << "服务器上下文不存在 (ID: " << server_id << ")" << std::endl;
return;
}
HttpServerCtx* ctx = it->second.get(); // 获取指针,不拷贝
lock.unlock(); // 手动解锁
asio::io_context io_context;
ctx->io_context = &io_context; // 关联IO上下文
try {
tcp::acceptor acceptor(io_context, tcp::endpoint(asio::ip::make_address(host), std::stoi(port)));
std::cout << "Server [" << server_id << "] listening on " << host << ":" << port << std::endl;
// 循环监听(支持退出)
while (ctx->running.load(std::memory_order_relaxed)) { // 原子操作读取
tcp::socket* socket = new tcp::socket(io_context);
try {
acceptor.accept(*socket);
// 启动客户端处理线程
std::thread(handle_client, std::move(socket), ctx->http_server_obj).detach();
}
catch (const asio::system_error& e) {
delete socket; // 中断时释放Socket
if (e.code() != asio::error::operation_aborted && ctx->running.load(std::memory_order_relaxed)) {
std::cerr << "Server [" << server_id << "] Accept error: " << e.what() << std::endl;
}
}
}
}
catch (const std::exception& e) {
std::cerr << "Server [" << server_id << "] error: " << e.what() << std::endl;
}
// 停止IO上下文
io_context.stop();
std::cout << "Server [" << server_id << "] stopped" << std::endl;
}
// ====================== Squirrel 绑定函数 - 创建HTTP客户端 ======================
static SQInteger CreateHttp(HSQUIRRELVM v) {
const SQChar* Host;
sq_getstring(v, 2, &Host);
const SQChar* Service;
sq_getstring(v, 3, &Service);
const SQChar* Content;
sq_getstring(v, 4, &Content);
try {
asio::io_context ioContext;
asio::ip::tcp::resolver resolver(ioContext);
auto endpoints = resolver.resolve(Host, Service);
asio::ip::tcp::socket socket(ioContext);
asio::connect(socket, endpoints);
// 发送HTTP请求
std::string request = Content;
asio::write(socket, asio::buffer(request));
// 读取响应
asio::streambuf response;
asio::error_code error;
while (asio::read(socket, response, asio::transfer_at_least(1), error)) {}
if (error && error != asio::error::eof) {
throw asio::system_error(error);
}
// 将响应内容返回
std::istream responseStream(&response);
std::ostringstream oss;
oss << responseStream.rdbuf();
sq_pushstring(v, oss.str().c_str(), -1);
return 1;
}
catch (const std::exception& e) {
return sq_throwerror(v, e.what());
}
catch (...) {
return sq_throwerror(v, _SC("Unknown error occurred"));
}
}
// ====================== Squirrel 绑定函数 - 创建HTTP服务器核心修复避免拷贝atomic ======================
static SQInteger CreateHttpServer(HSQUIRRELVM v) {
const SQChar* host = nullptr;
const SQChar* port = nullptr;
HSQOBJECT HttpServerObject;
// 1. 参数校验
if (SQ_FAILED(sq_getstring(v, 2, &host)) ||
SQ_FAILED(sq_getstring(v, 3, &port)) ||
SQ_FAILED(sq_getstackobj(v, 4, &HttpServerObject))) {
sq_pushstring(v, _SC("param error: need (host:str, port:str, obj:HSQOBJECT)"), -1);
return SQ_ERROR;
}
// 2. 生成唯一服务器ID
uint64_t server_id = g_next_server_id.fetch_add(1, std::memory_order_relaxed);
try {
// 3. 动态创建上下文(避免拷贝)
auto ctx_ptr = std::unique_ptr<HttpServerCtx>(new HttpServerCtx(server_id, v, HttpServerObject, std::string(host), std::string(port)));
sq_addref(v, &ctx_ptr->http_server_obj); // 增加引用
// 4. 加锁存入全局(移动语义,不拷贝)
std::lock_guard<std::mutex> lock(g_server_mutex);
g_server_ctxs[server_id] = std::move(ctx_ptr); // 移动unique_ptr不拷贝atomic
// 5. 启动服务器线程
std::thread server_thread(start_server, server_id, std::string(host), std::string(port));
server_thread.detach();
// 6. 返回唯一ID给Squirrel
sq_pushinteger(v, static_cast<SQInteger>(server_id));
return 1;
}
catch (...) {
// 异常清理
sq_release(v, &HttpServerObject); // 直接释放传入的对象
std::lock_guard<std::mutex> lock(g_server_mutex);
g_server_ctxs.erase(server_id);
sq_pushbool(v, false);
return 1;
}
}
// ====================== Squirrel 绑定函数 - 停止HTTP服务器 ======================
static SQInteger StopHttpServer(HSQUIRRELVM v) {
SQInteger server_id_int;
if (SQ_FAILED(sq_getinteger(v, 2, &server_id_int)) || server_id_int <= 0) {
sq_pushstring(v, _SC("invalid server ID"), -1);
return SQ_ERROR;
}
uint64_t server_id = static_cast<uint64_t>(server_id_int);
std::lock_guard<std::mutex> lock(g_server_mutex);
auto it = g_server_ctxs.find(server_id);
if (it == g_server_ctxs.end()) {
// 修正:拼接错误信息中的 server ID
std::string err_msg = "server instance not found (ID: " + std::to_string(server_id) + ")";
sq_pushstring(v, err_msg.c_str(), -1);
return SQ_ERROR;
}
HttpServerCtx* ctx = it->second.get();
// 1. 标记停止(原子操作)
ctx->running.store(false, std::memory_order_relaxed);
// 2. 强制停止 IO 上下文(中断所有异步操作)
if (ctx->io_context) {
ctx->io_context->stop();
}
// 3. 释放 Squirrel 引用
sq_release(ctx->vm, &ctx->http_server_obj);
// 4. 移除上下文unique_ptr 自动析构)
g_server_ctxs.erase(it);
sq_pushbool(v, true);
return 1;
}
// ====================== Squirrel 绑定函数 - 发送HTTP响应 ======================
static SQInteger HttpServerResponse_Write(HSQUIRRELVM v) {
SQUserPointer P;
if (SQ_FAILED(sq_getuserpointer(v, 2, &P))) {
return sq_throwerror(v, _SC("invalid socket pointer"));
}
tcp::socket* socket = static_cast<tcp::socket*>(P);
const SQChar* Content;
if (SQ_FAILED(sq_getstring(v, 3, &Content))) {
return sq_throwerror(v, _SC("invalid response content"));
}
try {
if (socket && socket->is_open()) {
std::string response = Content;
asio::write(*socket, asio::buffer(response));
}
}
catch (const std::exception& e) {
// 清理资源
if (socket) {
asio::error_code ec;
socket->close(ec);
delete socket;
}
return sq_throwerror(v, e.what());
}
catch (...) {
if (socket) {
asio::error_code ec;
socket->close(ec);
delete socket;
}
return sq_throwerror(v, _SC("Unknown error occurred"));
}
// 最后清理socket
if (socket) {
asio::error_code ec;
socket->close(ec);
delete socket;
}
return 0;
}
// ====================== 辅助函数 - 注册Squirrel函数 ======================
static SQInteger register_Dio_func(HSQUIRRELVM v, SQFUNCTION f, const char* fname) {
sq_pushroottable(v);
sq_pushstring(v, fname, -1);
sq_newclosure(v, f, 0);
sq_newslot(v, -3, SQFalse);
sq_pop(v, 1); // 弹出根表
return 0;
}
// ====================== 注册所有Dio函数 ======================
static void RegisterDio(HSQUIRRELVM v) {
// 创建HTTP客户端
register_Dio_func(v, CreateHttp, _SC("Sq_CreateHttp"));
// HTTP服务器相关
register_Dio_func(v, CreateHttpServer, _SC("Sq_CreateHttpServer"));
register_Dio_func(v, StopHttpServer, _SC("Sq_StopHttpServer"));
// 发送HTTP响应
register_Dio_func(v, HttpServerResponse_Write, _SC("Sq_HttpServerResponse_Write"));
}