1、添加connect.h和connect.c
connect.h
#ifndef _CONNECTION_H_
#define _CONNECTION_H_
#include<stdint.h>
#include<stddef.h>
#include"../include/protocol.h"
#define BUFFER_SIZE 65536
typedef struct {
int fd;
char in_buf[BUFFER_SIZE];
size_t in_pos;
uint32_t uid;
int is_auth;
} Conn;
extern Conn* g_conns[1024];
Conn* conn_new(int fd);
void conn_delete(int fd);
#endif
connect.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include"connection.h"
Conn* g_conns[1024] = {NULL};
Conn* conn_new(int fd){
if (fd<0 || fd >=1024) return NULL;
Conn* c = (Conn*)malloc(sizeof(Conn));
if(!c)return NULL;
memset(c,0,sizeof(Conn));
c->fd = fd;
g_conns[fd] = c;
return c;
}
void conn_delete(int fd){
if(fd<0 || fd>1024) return;
Conn* c = g_conns[fd];
if(c){
close(c->fd);
free(c);
g_conns[fd] = NULL;
}
}
2、改进server.c
server.c改进
void handle_login_request(Conn *conn, LoginRequest *req) {
printf(">>> [LOGIN] Username: %s, Password: %s\n", req->username, req->password);
// 构造响应
char send_buf[1024];
AppHeader *resp_header = (AppHeader *)send_buf;
LoginResponse *resp_body = (LoginResponse *)(send_buf + sizeof(AppHeader));
resp_body->uid = htonl(1001);
resp_body->result = 1;
strcpy(resp_body->msg, "Login Success (M1 Version)!");
resp_header->magic = htonl(PROTOCOL_MAGIC);
resp_header->version = htons(1);
resp_header->type = htons(MSG_TYPE_LOGIN_RESP);
resp_header->sequence = htonl(0);
resp_header->length = htonl(sizeof(LoginResponse));
// M1 阶段暂时直接 write,M2 阶段这里也会改成 output buffer
send(conn->fd, send_buf, sizeof(AppHeader) + sizeof(LoginResponse), 0);
}
void handle_client_message(int sockfd, int epoll_fd) {
Conn *conn = g_conns[sockfd];
if (!conn) return; // 保护性检查
// 1. 【读取阶段】尽可能多地把数据读到 in_buf 里
while (1) {
// 计算还有多少空间:总大小 - 当前已存的位置
size_t free_space = BUFFER_SIZE - conn->in_pos;
if (free_space == 0) {
printf("Error: Buffer full! Dropping connection.\n");
conn_delete(sockfd);
return;
}
ssize_t n = recv(sockfd, conn->in_buf + conn->in_pos, free_space, 0);
if (n > 0) {
conn->in_pos += n; // 推进指针
} else if (n == 0) {
printf("Client fd=%d disconnected\n", sockfd);
conn_delete(sockfd);
return;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break; // 数据读完了,跳出循环去处理
}
perror("Recv error");
conn_delete(sockfd);
return;
}
}
// 2. 【解析阶段】循环处理缓冲区里的数据 (解决粘包)
while (conn->in_pos >= sizeof(AppHeader)) {
// 预览头部
AppHeader *header = (AppHeader *)conn->in_buf;
uint32_t magic = ntohl(header->magic);
uint32_t payload_len = ntohl(header->length);
//安全检查
if (magic != PROTOCOL_MAGIC) {
printf("Error: Invalid Magic 0x%X. Kicking.\n", magic);
conn_delete(sockfd);
return;
}
// 检查是否“半包”:现有数据够不够一个完整的包
// 完整包长度 = 头部 + 负载
size_t total_frame_len = sizeof(AppHeader) + payload_len;
if (conn->in_pos < total_frame_len) {
// 数据不够,停止解析,等待下一次 recv 把剩下的拼起来
break;
}
// 3. 【分发阶段】已有完整的包
printf("Debug: Complete frame received. Type=%d, Len=%d\n",
ntohs(header->type), payload_len);
// 获取 Body 指针
void *body = conn->in_buf + sizeof(AppHeader);
uint16_t type = ntohs(header->type);
switch (type) {
case MSG_TYPE_LOGIN_REQ:
handle_login_request(conn, (LoginRequest *)body);
break;
case MSG_TYPE_CHAT_REQ:
printf(">>> [CHAT] Msg received.\n");
break;
default:
printf(">>> [UNKNOWN] Type: %d\n", type);
}
// 4. 【清理阶段】将处理完的数据移出缓冲区
// 将后面剩余的数据搬运到缓冲区头部
size_t remaining = conn->in_pos - total_frame_len;
if (remaining > 0) {
memmove(conn->in_buf, conn->in_buf + total_frame_len, remaining);
}
conn->in_pos = remaining; // 更新当前数据末尾指针
}
3、提升点
- 1、解决半包和粘包问题
先全部存进 conn->in_buf,然后在一个 while 循环里看缓冲区,处理完一块,用 memmove 把剩下的数据推到最前面,腾出空间
- 2、IO 层与业务层分离
- 3、
Conn结构体 (包含 fd, uid, buffer),为后续保存用户状态打下基础