1、客户端输入行一直间断刷新L-Chat > L-Chat > L-Chat > L-Chat > L-Chat >
1.1 问题分析
关键:刚启动客户端就会隔大概几十秒刷新一个L-Chat >,无论输入还是不输入 应该跟心跳线程有关系
void* recv_thread_func(void* arg) {
else if (type == MSG_TYPE_PING){
// 收到心跳回执,证明服务器还活着
// 工业级应用中,这里通常用来计算网络延迟 (RTT),或者重置客户端自己的断线重连计时器
// 这里选择“静默丢弃”,不打扰用户打字
continue;
}
printf("L-Chat > "); // 保持提示符
}
-
心跳线程(第 52 行)向服务器发送的是
MSG_TYPE_PING(探活)。 -
根据在服务端的协议定义,服务器收到
PING后,给客户端回复的是MSG_TYPE_PONG(确认回执)。 -
当接收线程收到包时,解析出来的
type是MSG_TYPE_PONG。 -
但是代码写的是
if (type == MSG_TYPE_PING),这个条件显然不成立(False) ! -
于是程序地绕过了
continue;,直接到了最底部的printf("L-Chat > ");。
笔误
1.2解决方案
把 MSG_TYPE_PING 改成 MSG_TYPE_PONG。
2、客户端1001给客户端1002发消息,1002未收到
2.1 问题分析
服务器
客户端1001
客户端1002
关键:Len:4294967287
chat_service.c
// 获取当前包的总长度,以此计算正文 content 的长度
// 技巧:回溯包头获取 length 字段:payload 指针直接指向 conn->in_buf 缓冲区里的数据,而包头(Header)正好紧紧挨在它前面,所以指针往前退 sizeof(AppHeader) 刚好能读到完美的包头。
AppHeader *header = (AppHeader *)((char *)payload - sizeof(AppHeader));//先转 char* 才能做指针算术
uint32_t total_payload_len = ntohl(header->length);
uint32_t content_len = total_payload_len - sizeof(ChatMsg);
- 在 M3 阶段(单线程) :这招很好用。因为
payload指针直接指向conn->in_buf缓冲区里的数据,而包头(Header)正好紧紧挨在它前面,所以指针往前退sizeof(AppHeader)刚好能读到完美的包头。
// ② 拷贝 payload 到堆内存
if (payload_len > 0)
{
new_task->payload =malloc(payload_len);
memcpy(new_task->payload,payload,payload_len);
}else{
new_task->payload = NULL;
}
在 M4 阶段(线程池) :为了防止主线程 epoll 覆盖数据,在 thread_pool_submit 里用 malloc 把 payload 拷贝到了堆(Heap) 中。
- 这时,
payload是一块全新的、干净的内存。 - 此时再把指针往前退
sizeof(AppHeader),实际上是指向了未分配的野内存(垃圾数据) ! - 读取到的
header->length是一个垃圾值(通常是 0)。 - 接着计算正文长度
0 - 9 = -9。在uint32_t(无符号 32 位整数)中,-9溢出后正好就是4294967287!
混合使用有符号和无符号整数时要格外小心!小心内存溢出
-
为什么 1002 收不到?
- 代码最后执行了
send(target_fd, header, 12 + 4294967287, 0)。 - (
send(target_conn->fd, (char *)header, sizeof(AppHeader) + total_payload_len, 0);) - 系统内核要发送 4.2 GB 的垃圾内存,直接报
EFAULT(地址越界)错误并拒绝发送。所以 1002 什么都收不到。
- 代码最后执行了
2.2 解决方案
既然 payload 已经是独立的内存了,我就不能再用指针回退的投机取巧方法,而是应该正经重新组装一个 Header。
void handle_chat(Conn *conn, void *payload) {
// ==========================================
//【修复】: 抛弃指针回退,通过 strlen 动态计算真实长度
// 正文长度 = 结构体大小 + 字符串长度 + 1(字符串结束符\0)
uint32_t payload_len = sizeof(ChatMsg) + strlen(msg->content) + 1;
// ==========================================
if (type == 0) { // 私聊
// ==========================================
// 【修复】:在栈上重新组装一个完整的响应包
char send_buf[1024];
AppHeader *header = (AppHeader *)send_buf;
header->magic = htonl(PROTOCOL_MAGIC);
header->version = htons(1);
header->type = htons(MSG_TYPE_CHAT_REQ);
header->length = htonl(payload_len);
// 把负载拷贝到 header 后面
memcpy(send_buf + sizeof(AppHeader), payload, payload_len);
// 发送完整的组装包
send(target_conn->fd, send_buf, sizeof(AppHeader) + payload_len, 0);
// ==========================================
}
2.2 测试结果
服务器
客户端1001
客户端1002