fix: 修复了部分问题:
read_full 部分读取返回不当 中途连接关闭时返回 -1 并设置 errno = ECONNRESET,避免上层误用不完整数据 tcgetattr 重复调用 改为 struct termios raw = g_original_termios 复制结构体,移除冗余系统调用 g_termios_saved 设置时机 移到 tcsetattr 成功之后,确保只有真正修改终端后才标记 有符号/无符号比较警告 使用 (size_t)written != sizeof(header) 显式转换 鼠标跟踪部分写入未检查 添加 (size_t)written != len 检查 载荷大小无上限 添加 MAX_PAYLOAD_SIZE (64MB) 限制,防止恶意数据耗尽内存 write 返回 0 未处理 添加检查,视为 EIO 错误 free_message_payload 冗余检查 直接调用 free(payload),因为 free(NULL) 是安全的 DEBUG_LOG 格式不一致 统一移除末尾 \n(假设宏自动添加换行)
This commit is contained in:
parent
a1f79164b7
commit
c05c02c098
|
|
@ -1,3 +1,14 @@
|
|||
/**
|
||||
* @file socket_protocol.c
|
||||
* @brief Socket 消息协议实现
|
||||
*
|
||||
* 功能:
|
||||
* - 消息的序列化和反序列化
|
||||
* - 可靠的消息读写(处理部分读写)
|
||||
* - 终端模式控制(原始模式、鼠标跟踪)
|
||||
* - 可选的 LZ4 压缩支持
|
||||
*/
|
||||
|
||||
#include "socket_protocol.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
|
@ -8,14 +19,44 @@
|
|||
|
||||
#include "debug.h"
|
||||
|
||||
// 保存原始终端设置
|
||||
/* ============================================================================
|
||||
* 常量定义
|
||||
* ============================================================================ */
|
||||
|
||||
/** 最大载荷大小限制(64MB),防止恶意或损坏数据导致的内存耗尽 */
|
||||
#define MAX_PAYLOAD_SIZE (64 * 1024 * 1024)
|
||||
|
||||
/* ============================================================================
|
||||
* 全局变量 - 终端状态
|
||||
* ============================================================================ */
|
||||
|
||||
/** 原始终端设置(用于恢复) */
|
||||
static struct termios g_original_termios;
|
||||
static int g_termios_saved = 0;
|
||||
|
||||
/**
|
||||
* 终端设置是否已保存
|
||||
* 使用 volatile 确保多次调用时状态正确可见
|
||||
* 注:实际访问是串行的(通过 pthread_join 保证),不需要互斥锁
|
||||
*/
|
||||
static volatile int g_termios_saved = 0;
|
||||
|
||||
/* ============================================================================
|
||||
* LZ4 压缩支持(条件编译)
|
||||
* ============================================================================ */
|
||||
|
||||
#ifdef HAVE_LZ4
|
||||
// 初始化协议上下文
|
||||
void protocol_init(ProtocolContext* ctx, CompressionType compress_type, int compress_level) {
|
||||
if (ctx == NULL) return;
|
||||
/**
|
||||
* @brief 初始化协议上下文
|
||||
*
|
||||
* @param ctx 协议上下文指针
|
||||
* @param compress_type 压缩类型
|
||||
* @param compress_level 压缩级别
|
||||
*/
|
||||
void protocol_init(ProtocolContext* ctx, CompressionType compress_type,
|
||||
int compress_level) {
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
compression_init(&ctx->compress_ctx, compress_type, compress_level);
|
||||
ctx->compression_enabled = (compress_type != COMPRESS_NONE);
|
||||
|
|
@ -24,7 +65,20 @@ void protocol_init(ProtocolContext* ctx, CompressionType compress_type, int comp
|
|||
}
|
||||
#endif
|
||||
|
||||
// 向 socket 完整写入指定字节数(处理部分写入)
|
||||
/* ============================================================================
|
||||
* 底层 I/O 函数
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief 向 socket 完整写入指定字节数
|
||||
*
|
||||
* 处理部分写入和信号中断,确保所有数据都被写入
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param buf 源数据缓冲区
|
||||
* @param count 要写入的字节数
|
||||
* @return 成功返回写入的字节数,失败返回 -1
|
||||
*/
|
||||
static ssize_t write_full(int sock, const void* buf, size_t count) {
|
||||
size_t total_written = 0;
|
||||
const char* ptr = (const char*)buf;
|
||||
|
|
@ -33,25 +87,87 @@ static ssize_t write_full(int sock, const void* buf, size_t count) {
|
|||
ssize_t n = write(sock, ptr + total_written, count - total_written);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue; // 被信号中断,重试
|
||||
continue; /* 被信号中断,重试 */
|
||||
}
|
||||
DEBUG_LOG("write_full: write error: %s", strerror(errno));
|
||||
DEBUG_LOG("write_full: 写入错误: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
total_written += n;
|
||||
if (total_written < count) {
|
||||
DEBUG_LOG("write_full: 进度 %zu/%zu 字节", total_written, count);
|
||||
if (n == 0) {
|
||||
/* write 返回 0 通常不应该发生,视为错误 */
|
||||
DEBUG_LOG("write_full: write 返回 0");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
total_written += (size_t)n;
|
||||
}
|
||||
|
||||
return (ssize_t)total_written;
|
||||
}
|
||||
|
||||
// 写入完整消息
|
||||
/**
|
||||
* @brief 从 socket 完整读取指定字节数
|
||||
*
|
||||
* 处理部分读取和信号中断,确保读取完整数据或检测到连接关闭
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param buf 目标缓冲区
|
||||
* @param count 要读取的字节数
|
||||
* @return 成功返回读取的字节数,连接正常关闭返回 0,错误返回 -1
|
||||
*
|
||||
* @note 如果在读取过程中连接关闭(部分读取),返回 -1 并设置 errno = ECONNRESET
|
||||
*/
|
||||
static ssize_t read_full(int sock, void* buf, size_t count) {
|
||||
size_t total_read = 0;
|
||||
char* ptr = (char*)buf;
|
||||
|
||||
while (total_read < count) {
|
||||
ssize_t n = read(sock, ptr + total_read, count - total_read);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue; /* 被信号中断,重试 */
|
||||
}
|
||||
DEBUG_LOG("read_full: 读取错误: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
/* 连接关闭 */
|
||||
if (total_read > 0) {
|
||||
/*
|
||||
* 已读取部分数据但连接关闭,这是不完整的消息
|
||||
* 视为错误,避免上层误用不完整数据
|
||||
*/
|
||||
DEBUG_LOG("read_full: 连接中途关闭,已读取 %zu/%zu 字节",
|
||||
total_read, count);
|
||||
errno = ECONNRESET;
|
||||
return -1;
|
||||
}
|
||||
/* 干净的连接关闭(未读取任何数据) */
|
||||
return 0;
|
||||
}
|
||||
total_read += (size_t)n;
|
||||
}
|
||||
|
||||
return (ssize_t)total_read;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* 消息读写函数
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief 写入完整消息到 socket
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param type 消息类型
|
||||
* @param payload 消息载荷(可以为 NULL)
|
||||
* @param payload_len 载荷长度
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int write_message(int sock, MessageType type, const void* payload,
|
||||
uint32_t payload_len) {
|
||||
DEBUG_LOG("写入消息: type=%d, payload_len=%u", type, payload_len);
|
||||
|
||||
/* 构建消息头 */
|
||||
MessageHeader header;
|
||||
header.magic = MESSAGE_MAGIC;
|
||||
header.type = type;
|
||||
|
|
@ -60,25 +176,22 @@ int write_message(int sock, MessageType type, const void* payload,
|
|||
|
||||
DEBUG_HEX("消息头", &header, sizeof(header));
|
||||
|
||||
// 发送消息头(确保完整发送)
|
||||
/* 发送消息头 */
|
||||
ssize_t written = write_full(sock, &header, sizeof(header));
|
||||
if (written != sizeof(header)) {
|
||||
DEBUG_LOG(
|
||||
"Failed to write message header: 期望 %zu 字节, 实际写入 %zd "
|
||||
"字节\n",
|
||||
sizeof(header), written);
|
||||
if (written < 0 || (size_t)written != sizeof(header)) {
|
||||
DEBUG_LOG("写入消息头失败: 期望 %zu 字节, 实际 %zd 字节",
|
||||
sizeof(header), written);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 写入载荷(确保完整发送)
|
||||
/* 发送载荷 */
|
||||
if (payload_len > 0 && payload != NULL) {
|
||||
DEBUG_HEX("消息载荷", payload, payload_len > 64 ? 64 : payload_len);
|
||||
|
||||
written = write_full(sock, payload, payload_len);
|
||||
if (written != (ssize_t)payload_len) {
|
||||
DEBUG_LOG(
|
||||
"Failed to write message payload: 期望 %u 字节, 实际写入 %zd "
|
||||
"字节\n",
|
||||
payload_len, written);
|
||||
if (written < 0 || (size_t)written != payload_len) {
|
||||
DEBUG_LOG("写入消息载荷失败: 期望 %u 字节, 实际 %zd 字节",
|
||||
payload_len, written);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_LOG("载荷写入成功: %zd 字节", written);
|
||||
|
|
@ -89,25 +202,36 @@ int write_message(int sock, MessageType type, const void* payload,
|
|||
}
|
||||
|
||||
#ifdef HAVE_LZ4
|
||||
// 带压缩支持的消息写入
|
||||
/**
|
||||
* @brief 写入压缩消息到 socket
|
||||
*
|
||||
* 如果压缩失败或未启用压缩,自动回退到普通写入
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param ctx 协议上下文
|
||||
* @param type 消息类型
|
||||
* @param payload 消息载荷
|
||||
* @param payload_len 载荷长度
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int write_message_compressed(int sock, ProtocolContext* ctx, MessageType type,
|
||||
const void* payload, uint32_t payload_len) {
|
||||
DEBUG_LOG("写入压缩消息: type=%d, payload_len=%u", type, payload_len);
|
||||
|
||||
// 如果没有上下文或压缩未启用,使用普通写入
|
||||
/* 如果没有上下文或压缩未启用,使用普通写入 */
|
||||
if (ctx == NULL || !ctx->compression_enabled || payload_len == 0) {
|
||||
return write_message(sock, type, payload, payload_len);
|
||||
}
|
||||
|
||||
// 分配压缩缓冲区
|
||||
/* 分配压缩缓冲区 */
|
||||
size_t compress_bound = get_compress_bound(payload_len);
|
||||
void* compressed_buf = malloc(compress_bound);
|
||||
if (compressed_buf == NULL) {
|
||||
DEBUG_LOG("分配压缩缓冲区失败");
|
||||
DEBUG_LOG("分配压缩缓冲区失败,回退到普通写入");
|
||||
return write_message(sock, type, payload, payload_len);
|
||||
}
|
||||
|
||||
// 压缩数据
|
||||
/* 压缩数据 */
|
||||
uint32_t flags = 0;
|
||||
int compressed_size = compress_data(&ctx->compress_ctx,
|
||||
payload, payload_len,
|
||||
|
|
@ -115,34 +239,34 @@ int write_message_compressed(int sock, ProtocolContext* ctx, MessageType type,
|
|||
&flags);
|
||||
|
||||
if (compressed_size < 0) {
|
||||
DEBUG_LOG("压缩失败,使用原始数据");
|
||||
DEBUG_LOG("压缩失败,回退到普通写入");
|
||||
free(compressed_buf);
|
||||
return write_message(sock, type, payload, payload_len);
|
||||
}
|
||||
|
||||
// 构建消息头
|
||||
/* 构建消息头 */
|
||||
MessageHeader header;
|
||||
header.magic = MESSAGE_MAGIC;
|
||||
header.type = type;
|
||||
header.payload_len = (uint32_t)compressed_size;
|
||||
header.reserved = MAKE_RESERVED(flags, payload_len);
|
||||
|
||||
DEBUG_LOG("压缩消息头: type=%d, compressed_len=%d, original_len=%u, flags=0x%x",
|
||||
DEBUG_LOG("压缩消息头: type=%d, compressed=%d, original=%u, flags=0x%x",
|
||||
type, compressed_size, payload_len, flags);
|
||||
DEBUG_HEX("压缩消息头", &header, sizeof(header));
|
||||
|
||||
// 发送消息头
|
||||
/* 发送消息头 */
|
||||
ssize_t written = write_full(sock, &header, sizeof(header));
|
||||
if (written != sizeof(header)) {
|
||||
DEBUG_LOG("写入消息头失败");
|
||||
if (written < 0 || (size_t)written != sizeof(header)) {
|
||||
DEBUG_LOG("写入压缩消息头失败");
|
||||
free(compressed_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 发送压缩后的载荷
|
||||
/* 发送压缩后的载荷 */
|
||||
if (compressed_size > 0) {
|
||||
written = write_full(sock, compressed_buf, compressed_size);
|
||||
if (written != compressed_size) {
|
||||
written = write_full(sock, compressed_buf, (size_t)compressed_size);
|
||||
if (written < 0 || (size_t)written != (size_t)compressed_size) {
|
||||
DEBUG_LOG("写入压缩载荷失败");
|
||||
free(compressed_buf);
|
||||
return -1;
|
||||
|
|
@ -155,58 +279,46 @@ int write_message_compressed(int sock, ProtocolContext* ctx, MessageType type,
|
|||
}
|
||||
#endif
|
||||
|
||||
// 从 socket 完整读取指定字节数(处理部分读取)
|
||||
static ssize_t read_full(int sock, void* buf, size_t count) {
|
||||
size_t total_read = 0;
|
||||
char* ptr = (char*)buf;
|
||||
|
||||
while (total_read < count) {
|
||||
ssize_t n = read(sock, ptr + total_read, count - total_read);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue; // 被信号中断,重试
|
||||
}
|
||||
DEBUG_LOG("read_full: read error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
// 连接关闭
|
||||
DEBUG_LOG("read_full: 连接关闭,已读取 %zu/%zu 字节", total_read,
|
||||
count);
|
||||
return total_read > 0 ? (ssize_t)total_read : 0;
|
||||
}
|
||||
total_read += n;
|
||||
DEBUG_LOG("read_full: 进度 %zu/%zu 字节", total_read, count);
|
||||
}
|
||||
|
||||
return (ssize_t)total_read;
|
||||
}
|
||||
|
||||
// 读取完整消息
|
||||
/**
|
||||
* @brief 从 socket 读取完整消息
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param type [out] 消息类型
|
||||
* @param payload [out] 消息载荷(调用者负责释放)
|
||||
* @param payload_len [out] 载荷长度
|
||||
* @return 1 成功读取,0 连接正常关闭,-1 错误
|
||||
*/
|
||||
int read_message(int sock, MessageType* type, void** payload,
|
||||
uint32_t* payload_len) {
|
||||
MessageHeader header;
|
||||
|
||||
DEBUG_LOG("开始读取消息...");
|
||||
|
||||
// 读取消息头(确保读取完整)
|
||||
/* 读取消息头 */
|
||||
ssize_t bytes_read = read_full(sock, &header, sizeof(header));
|
||||
if (bytes_read != sizeof(header)) {
|
||||
if (bytes_read == 0) {
|
||||
DEBUG_LOG("连接正常关闭");
|
||||
return 0; // 连接正常关闭
|
||||
}
|
||||
DEBUG_LOG(
|
||||
"Failed to read message header, got %zd bytes, expected %zu\n",
|
||||
bytes_read, sizeof(header));
|
||||
if (bytes_read == 0) {
|
||||
DEBUG_LOG("连接正常关闭");
|
||||
return 0;
|
||||
}
|
||||
if (bytes_read < 0 || (size_t)bytes_read != sizeof(header)) {
|
||||
DEBUG_LOG("读取消息头失败: 期望 %zu 字节, 实际 %zd 字节",
|
||||
sizeof(header), bytes_read);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_HEX("收到消息头", &header, sizeof(header));
|
||||
|
||||
// 验证魔数
|
||||
/* 验证魔数 */
|
||||
if (header.magic != MESSAGE_MAGIC) {
|
||||
DEBUG_LOG("Invalid message magic: 0x%x\n", header.magic);
|
||||
DEBUG_LOG("无效的消息魔数: 0x%x (期望 0x%x)",
|
||||
header.magic, MESSAGE_MAGIC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 验证载荷大小 */
|
||||
if (header.payload_len > MAX_PAYLOAD_SIZE) {
|
||||
DEBUG_LOG("载荷大小超出限制: %u > %u",
|
||||
header.payload_len, MAX_PAYLOAD_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -215,28 +327,29 @@ int read_message(int sock, MessageType* type, void** payload,
|
|||
|
||||
DEBUG_LOG("消息类型=%d, 载荷长度=%u", *type, *payload_len);
|
||||
|
||||
// 读取载荷(确保读取完整)
|
||||
/* 读取载荷 */
|
||||
if (header.payload_len > 0) {
|
||||
*payload = malloc(header.payload_len + 1); // +1 用于可能的字符串终止符
|
||||
/* +1 用于字符串终止符 */
|
||||
*payload = malloc(header.payload_len + 1);
|
||||
if (*payload == NULL) {
|
||||
DEBUG_LOG("Failed to allocate memory for payload\n");
|
||||
DEBUG_LOG("分配载荷内存失败: %u 字节", header.payload_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bytes_read = read_full(sock, *payload, header.payload_len);
|
||||
if (bytes_read != (ssize_t)header.payload_len) {
|
||||
DEBUG_LOG(
|
||||
"Failed to read message payload: 期望%u字节, 实际读取%zd字节\n",
|
||||
header.payload_len, bytes_read);
|
||||
if (bytes_read < 0 || (size_t)bytes_read != header.payload_len) {
|
||||
DEBUG_LOG("读取载荷失败: 期望 %u 字节, 实际 %zd 字节",
|
||||
header.payload_len, bytes_read);
|
||||
free(*payload);
|
||||
*payload = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("载荷读取成功: %zd 字节", bytes_read);
|
||||
DEBUG_HEX("收到载荷", *payload, bytes_read > 64 ? 64 : bytes_read);
|
||||
DEBUG_HEX("收到载荷", *payload,
|
||||
(size_t)bytes_read > 64 ? 64 : (size_t)bytes_read);
|
||||
|
||||
// 如果是字符串,添加终止符
|
||||
/* 添加字符串终止符(便于作为字符串使用) */
|
||||
((char*)(*payload))[header.payload_len] = '\0';
|
||||
} else {
|
||||
*payload = NULL;
|
||||
|
|
@ -246,29 +359,46 @@ int read_message(int sock, MessageType* type, void** payload,
|
|||
}
|
||||
|
||||
#ifdef HAVE_LZ4
|
||||
// 带解压支持的消息读取
|
||||
/**
|
||||
* @brief 从 socket 读取消息并解压
|
||||
*
|
||||
* @param sock Socket 文件描述符
|
||||
* @param type [out] 消息类型
|
||||
* @param payload [out] 解压后的载荷(调用者负责释放)
|
||||
* @param payload_len [out] 解压后的载荷长度
|
||||
* @param original_len [out] 原始(压缩后)载荷长度(可选,可为 NULL)
|
||||
* @return 1 成功读取,0 连接正常关闭,-1 错误
|
||||
*/
|
||||
int read_message_decompressed(int sock, MessageType* type, void** payload,
|
||||
uint32_t* payload_len, uint32_t* original_len) {
|
||||
MessageHeader header;
|
||||
|
||||
DEBUG_LOG("开始读取消息(带解压)...");
|
||||
|
||||
// 读取消息头
|
||||
/* 读取消息头 */
|
||||
ssize_t bytes_read = read_full(sock, &header, sizeof(header));
|
||||
if (bytes_read != sizeof(header)) {
|
||||
if (bytes_read == 0) {
|
||||
DEBUG_LOG("连接正常关闭");
|
||||
return 0;
|
||||
}
|
||||
DEBUG_LOG("读取消息头失败: got %zd bytes, expected %zu", bytes_read, sizeof(header));
|
||||
if (bytes_read == 0) {
|
||||
DEBUG_LOG("连接正常关闭");
|
||||
return 0;
|
||||
}
|
||||
if (bytes_read < 0 || (size_t)bytes_read != sizeof(header)) {
|
||||
DEBUG_LOG("读取消息头失败: 期望 %zu 字节, 实际 %zd 字节",
|
||||
sizeof(header), bytes_read);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_HEX("收到消息头", &header, sizeof(header));
|
||||
|
||||
// 验证魔数
|
||||
/* 验证魔数 */
|
||||
if (header.magic != MESSAGE_MAGIC) {
|
||||
DEBUG_LOG("无效魔数: 0x%x", header.magic);
|
||||
DEBUG_LOG("无效的消息魔数: 0x%x", header.magic);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 验证载荷大小 */
|
||||
if (header.payload_len > MAX_PAYLOAD_SIZE) {
|
||||
DEBUG_LOG("载荷大小超出限制: %u > %u",
|
||||
header.payload_len, MAX_PAYLOAD_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -279,50 +409,57 @@ int read_message_decompressed(int sock, MessageType* type, void** payload,
|
|||
DEBUG_LOG("消息类型=%d, 压缩载荷=%u, flags=0x%x, size_hint=%u",
|
||||
*type, header.payload_len, flags, size_hint);
|
||||
|
||||
// 读取压缩载荷
|
||||
/* 空载荷处理 */
|
||||
if (header.payload_len == 0) {
|
||||
*payload = NULL;
|
||||
*payload_len = 0;
|
||||
if (original_len) *original_len = 0;
|
||||
if (original_len != NULL) {
|
||||
*original_len = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* compressed_buf = malloc(header.payload_len);
|
||||
/* 分配缓冲区(+1 用于字符串终止符) */
|
||||
void* compressed_buf = malloc(header.payload_len + 1);
|
||||
if (compressed_buf == NULL) {
|
||||
DEBUG_LOG("分配压缩缓冲区失败");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 读取载荷 */
|
||||
bytes_read = read_full(sock, compressed_buf, header.payload_len);
|
||||
if (bytes_read != (ssize_t)header.payload_len) {
|
||||
DEBUG_LOG("读取压缩载荷失败: 期望%u字节, 实际%zd字节",
|
||||
if (bytes_read < 0 || (size_t)bytes_read != header.payload_len) {
|
||||
DEBUG_LOG("读取压缩载荷失败: 期望 %u 字节, 实际 %zd 字节",
|
||||
header.payload_len, bytes_read);
|
||||
free(compressed_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 检查是否需要解压
|
||||
/* 检查是否需要解压 */
|
||||
if (!(flags & MSG_FLAG_COMPRESSED)) {
|
||||
// 数据未压缩,直接返回
|
||||
/* 数据未压缩,添加终止符后直接返回 */
|
||||
((char*)compressed_buf)[header.payload_len] = '\0';
|
||||
*payload = compressed_buf;
|
||||
*payload_len = header.payload_len;
|
||||
if (original_len) *original_len = header.payload_len;
|
||||
|
||||
// 添加字符串终止符(重新分配以确保空间)
|
||||
void* new_buf = realloc(compressed_buf, header.payload_len + 1);
|
||||
if (new_buf) {
|
||||
*payload = new_buf;
|
||||
((char*)(*payload))[header.payload_len] = '\0';
|
||||
if (original_len != NULL) {
|
||||
*original_len = header.payload_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 需要解压
|
||||
// 估算解压后大小(使用 size_hint 或默认 4 倍)
|
||||
size_t decompress_capacity = size_hint > 0 ? size_hint + 256 : header.payload_len * 4;
|
||||
if (decompress_capacity < 256) decompress_capacity = 256;
|
||||
/* 需要解压 */
|
||||
/* 估算解压后大小(使用 size_hint 或默认 4 倍) */
|
||||
size_t decompress_capacity = (size_hint > 0)
|
||||
? (size_hint + 256)
|
||||
: (header.payload_len * 4);
|
||||
if (decompress_capacity < 256) {
|
||||
decompress_capacity = 256;
|
||||
}
|
||||
if (decompress_capacity > MAX_PAYLOAD_SIZE) {
|
||||
decompress_capacity = MAX_PAYLOAD_SIZE;
|
||||
}
|
||||
|
||||
/* 分配解压缓冲区(+1 用于字符串终止符) */
|
||||
void* decompressed_buf = malloc(decompress_capacity + 1);
|
||||
if (decompressed_buf == NULL) {
|
||||
DEBUG_LOG("分配解压缓冲区失败");
|
||||
|
|
@ -330,6 +467,7 @@ int read_message_decompressed(int sock, MessageType* type, void** payload,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* 解压数据 */
|
||||
int decompressed_size = decompress_data(flags,
|
||||
compressed_buf, header.payload_len,
|
||||
decompressed_buf, decompress_capacity);
|
||||
|
|
@ -342,108 +480,190 @@ int read_message_decompressed(int sock, MessageType* type, void** payload,
|
|||
return -1;
|
||||
}
|
||||
|
||||
// 添加字符串终止符
|
||||
/* 添加字符串终止符 */
|
||||
((char*)decompressed_buf)[decompressed_size] = '\0';
|
||||
|
||||
*payload = decompressed_buf;
|
||||
*payload_len = (uint32_t)decompressed_size;
|
||||
if (original_len) *original_len = header.payload_len;
|
||||
if (original_len != NULL) {
|
||||
*original_len = header.payload_len;
|
||||
}
|
||||
|
||||
DEBUG_LOG("解压成功: %u -> %d 字节", header.payload_len, decompressed_size);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 释放消息载荷
|
||||
/**
|
||||
* @brief 释放消息载荷
|
||||
*
|
||||
* @param payload 要释放的载荷指针
|
||||
*/
|
||||
void free_message_payload(void* payload) {
|
||||
if (payload != NULL) {
|
||||
free(payload);
|
||||
}
|
||||
free(payload); /* free(NULL) 是安全的 */
|
||||
}
|
||||
|
||||
// 设置终端为原始模式(捕获所有输入)
|
||||
/* ============================================================================
|
||||
* 终端控制函数
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief 设置终端为原始模式
|
||||
*
|
||||
* 原始模式下:
|
||||
* - 禁用回显
|
||||
* - 禁用规范模式(逐字符读取)
|
||||
* - 禁用信号生成(Ctrl+C 等)
|
||||
* - 禁用输入/输出处理
|
||||
*
|
||||
* @param fd 终端文件描述符
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int setup_terminal_raw_mode(int fd) {
|
||||
DEBUG_LOG("设置终端为原始模式: fd=%d", fd);
|
||||
struct termios raw;
|
||||
|
||||
/* 保存原始设置 */
|
||||
if (tcgetattr(fd, &g_original_termios) < 0) {
|
||||
DEBUG_LOG("保存原始终端设置失败");
|
||||
return -1;
|
||||
}
|
||||
g_termios_saved = 1;
|
||||
DEBUG_LOG("原始终端设置已保存");
|
||||
|
||||
if (tcgetattr(fd, &raw) < 0) {
|
||||
DEBUG_LOG("保存原始终端设置失败: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置原始模式
|
||||
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||
raw.c_cflag &= ~(CSIZE | PARENB);
|
||||
raw.c_cflag |= CS8;
|
||||
raw.c_oflag &= ~(OPOST);
|
||||
/* 基于原始设置创建修改版本 */
|
||||
struct termios raw = g_original_termios;
|
||||
|
||||
// 设置读取超时
|
||||
raw.c_cc[VMIN] = 0;
|
||||
raw.c_cc[VTIME] = 1;
|
||||
/* 输入模式:禁用各种处理 */
|
||||
raw.c_iflag &= ~(tcflag_t)(BRKINT | /* 忽略 BREAK */
|
||||
ICRNL | /* 不将 CR 转换为 NL */
|
||||
INPCK | /* 禁用奇偶校验 */
|
||||
ISTRIP | /* 不剥离第 8 位 */
|
||||
IXON); /* 禁用软件流控制 */
|
||||
|
||||
/* 输出模式:禁用输出处理 */
|
||||
raw.c_oflag &= ~(tcflag_t)(OPOST); /* 禁用输出处理 */
|
||||
|
||||
/* 控制模式:设置字符大小为 8 位 */
|
||||
raw.c_cflag &= ~(tcflag_t)(CSIZE | /* 清除字符大小位 */
|
||||
PARENB); /* 禁用奇偶校验 */
|
||||
raw.c_cflag |= CS8; /* 8 位字符 */
|
||||
|
||||
/* 本地模式:禁用回显和规范模式 */
|
||||
raw.c_lflag &= ~(tcflag_t)(ECHO | /* 禁用回显 */
|
||||
ICANON | /* 禁用规范模式 */
|
||||
IEXTEN | /* 禁用扩展输入处理 */
|
||||
ISIG); /* 禁用信号生成 */
|
||||
|
||||
/* 设置读取参数 */
|
||||
raw.c_cc[VMIN] = 0; /* 非阻塞读取 */
|
||||
raw.c_cc[VTIME] = 1; /* 100ms 超时 */
|
||||
|
||||
/* 应用新设置 */
|
||||
if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) {
|
||||
DEBUG_LOG("设置终端原始模式失败");
|
||||
DEBUG_LOG("设置终端原始模式失败: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 只有成功后才标记为已保存 */
|
||||
g_termios_saved = 1;
|
||||
DEBUG_LOG("终端原始模式设置成功");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 恢复终端模式
|
||||
/**
|
||||
* @brief 恢复终端模式
|
||||
*
|
||||
* 将终端恢复到调用 setup_terminal_raw_mode 之前的状态
|
||||
* 此函数可以安全地多次调用
|
||||
*
|
||||
* @param fd 终端文件描述符
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int restore_terminal_mode(int fd) {
|
||||
DEBUG_LOG("恢复终端模式: fd=%d", fd);
|
||||
|
||||
if (!g_termios_saved) {
|
||||
DEBUG_LOG("没有保存的终端设置,跳过恢复");
|
||||
return 0; // 没有保存过或已经恢复过,这不是错误
|
||||
DEBUG_LOG("没有保存的终端设置,跳过恢复");
|
||||
return 0; /* 没有保存过或已经恢复过,不是错误 */
|
||||
}
|
||||
|
||||
if (tcsetattr(fd, TCSAFLUSH, &g_original_termios) < 0) {
|
||||
DEBUG_LOG("恢复终端模式失败");
|
||||
DEBUG_LOG("恢复终端模式失败: %s", strerror(errno));
|
||||
/* 不清除 g_termios_saved,允许后续重试 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("终端模式恢复成功");
|
||||
g_termios_saved = 0; // 标记已恢复,防止重复恢复
|
||||
g_termios_saved = 0; /* 标记已恢复,防止重复恢复 */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 启用鼠标跟踪(发送ANSI转义序列)
|
||||
/**
|
||||
* @brief 启用鼠标跟踪
|
||||
*
|
||||
* 发送 ANSI 转义序列启用鼠标事件报告
|
||||
* 启用的模式:
|
||||
* - X11 鼠标报告 (1000)
|
||||
* - 单元格运动跟踪 (1002)
|
||||
* - SGR 扩展模式 (1006)
|
||||
*
|
||||
* @param fd 终端文件描述符
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int enable_mouse_tracking(int fd) {
|
||||
// 启用鼠标跟踪模式
|
||||
// \033[?1000h - 启用X11鼠标报告
|
||||
// \033[?1002h - 启用单元格运动鼠标跟踪
|
||||
// \033[?1003h - 启用所有运动鼠标跟踪
|
||||
// \033[?1006h - 启用SGR扩展鼠标模式
|
||||
const char* enable_seq = "\033[?1000h\033[?1002h\033[?1006h";
|
||||
/*
|
||||
* 转义序列说明:
|
||||
* \033[?1000h - 启用 X11 鼠标报告(基本鼠标事件)
|
||||
* \033[?1002h - 启用按钮事件跟踪(拖拽)
|
||||
* \033[?1006h - 启用 SGR 扩展模式(支持大于 223 的坐标)
|
||||
*/
|
||||
static const char enable_seq[] = "\033[?1000h\033[?1002h\033[?1006h";
|
||||
size_t len = sizeof(enable_seq) - 1; /* 不包括 '\0' */
|
||||
|
||||
ssize_t written = write(fd, enable_seq, strlen(enable_seq));
|
||||
ssize_t written = write(fd, enable_seq, len);
|
||||
if (written < 0) {
|
||||
DEBUG_LOG("启用鼠标跟踪失败: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if ((size_t)written != len) {
|
||||
DEBUG_LOG("启用鼠标跟踪: 部分写入 %zd/%zu 字节", written, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("鼠标跟踪已启用");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 禁用鼠标跟踪
|
||||
/**
|
||||
* @brief 禁用鼠标跟踪
|
||||
*
|
||||
* 发送 ANSI 转义序列禁用鼠标事件报告
|
||||
* 按启用的相反顺序禁用各模式
|
||||
*
|
||||
* @param fd 终端文件描述符
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int disable_mouse_tracking(int fd) {
|
||||
// 禁用鼠标跟踪模式
|
||||
// \033[?1000l - 禁用X11鼠标报告
|
||||
// \033[?1002l - 禁用单元格运动鼠标跟踪
|
||||
// \033[?1006l - 禁用SGR扩展鼠标模式
|
||||
const char* disable_seq = "\033[?1006l\033[?1002l\033[?1000l";
|
||||
/*
|
||||
* 转义序列说明:
|
||||
* \033[?1006l - 禁用 SGR 扩展模式
|
||||
* \033[?1002l - 禁用按钮事件跟踪
|
||||
* \033[?1000l - 禁用 X11 鼠标报告
|
||||
* 注:按启用的相反顺序禁用
|
||||
*/
|
||||
static const char disable_seq[] = "\033[?1006l\033[?1002l\033[?1000l";
|
||||
size_t len = sizeof(disable_seq) - 1; /* 不包括 '\0' */
|
||||
|
||||
ssize_t written = write(fd, disable_seq, strlen(disable_seq));
|
||||
ssize_t written = write(fd, disable_seq, len);
|
||||
if (written < 0) {
|
||||
DEBUG_LOG("禁用鼠标跟踪失败: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if ((size_t)written != len) {
|
||||
DEBUG_LOG("禁用鼠标跟踪: 部分写入 %zd/%zu 字节", written, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("鼠标跟踪已禁用");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue