C++11实现TCP网络通讯服务端处理逻辑简化版
以下是使用C++11实现的TCP服务端处理逻辑,包含循环读取数据、帧头检测(AABBCC)及4376字节数据包处理:
cpp
#include <iostream> | |
#include <vector> | |
#include <cstring> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#include <sys/socket.h> | |
constexpr size_t PACKET_SIZE = 4376; // 完整数据包大小(含3字节帧头) | |
constexpr size_t HEADER_SIZE = 3; // 帧头大小 | |
constexpr uint8_t FRAME_HEADER[] = {0xAA, 0xBB, 0xCC}; // 帧头标识 | |
// 查找缓冲区中的帧头位置 | |
auto find_frame_header(const std::vector<char>& buffer) { | |
for (size_t i = 0; i <= buffer.size() - HEADER_SIZE; ++i) { | |
bool match = true; | |
for (size_t j = 0; j < HEADER_SIZE; ++j) { | |
if (static_cast<uint8_t>(buffer[i+j]) != FRAME_HEADER[j]) { | |
match = false; | |
break; | |
} | |
} | |
if (match) return buffer.begin() + i; | |
} | |
return buffer.end(); | |
} | |
// 数据包处理函数(示例) | |
void process_packet(const char* data) { | |
// 这里添加实际业务逻辑 | |
std::cout << "Received valid packet!" << std::endl; | |
} | |
int main() { | |
// 1. 创建TCP socket | |
int server_fd = socket(AF_INET, SOCK_STREAM, 0); | |
if (server_fd == -1) { | |
std::cerr << "Socket creation failed!" << std::endl; | |
return 1; | |
} | |
// 2. 绑定地址 | |
sockaddr_in server_addr{}; | |
server_addr.sin_family = AF_INET; | |
server_addr.sin_addr.s_addr = INADDR_ANY; | |
server_addr.sin_port = htons(8080); // 监听端口 | |
if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) == -1) { | |
std::cerr << "Bind failed!" << std::endl; | |
close(server_fd); | |
return 1; | |
} | |
// 3. 开始监听 | |
if (listen(server_fd, 5) == -1) { | |
std::cerr << "Listen failed!" << std::endl; | |
close(server_fd); | |
return 1; | |
} | |
// 4. 接受客户端连接 | |
sockaddr_in client_addr{}; | |
socklen_t addr_len = sizeof(client_addr); | |
int client_fd = accept(server_fd, (sockaddr*)&client_addr, &addr_len); | |
if (client_fd == -1) { | |
std::cerr << "Accept failed!" << std::endl; | |
close(server_fd); | |
return 1; | |
} | |
std::vector<char> buffer; | |
buffer.reserve(PACKET_SIZE * 2); // 预留双倍数据包空间 | |
while (true) { | |
// 5. 循环接收数据 | |
char temp_buf[1024]; | |
ssize_t bytes_received = recv(client_fd, temp_buf, sizeof(temp_buf), 0); | |
if (bytes_received <= 0) { | |
std::cout << "Connection closed by client" << std::endl; | |
break; | |
} | |
// 将接收数据追加到缓冲区 | |
buffer.insert(buffer.end(), temp_buf, temp_buf + bytes_received); | |
// 6. 处理缓冲区数据 | |
while (true) { | |
auto header_pos = find_frame_header(buffer); | |
if (header_pos == buffer.end()) { | |
// 未找到帧头,保留最后两个字节防止丢弃可能的帧头前缀 | |
if (buffer.size() >= HEADER_SIZE - 1) { | |
buffer.erase(buffer.begin(), buffer.end() - (HEADER_SIZE - 1)); | |
} else { | |
buffer.clear(); | |
} | |
break; | |
} | |
// 检查剩余数据是否足够 | |
if (buffer.end() - header_pos >= PACKET_SIZE) { | |
// 7. 处理完整数据包 | |
process_packet(&*header_pos); | |
// 8. 移除已处理数据(包含帧头) | |
buffer.erase(buffer.begin(), header_pos + PACKET_SIZE); | |
} else { | |
// 数据不足,等待下次接收 | |
break; | |
} | |
} | |
} | |
// 清理资源 | |
close(client_fd); | |
close(server_fd); | |
return 0; | |
} |
关键实现细节说明:
- 缓冲区管理:
- 使用
std::vector<char>
作为动态缓冲区,自动管理内存 - 预分配双倍数据包大小空间(
PACKET_SIZE * 2
)减少内存重分配
- 使用
- 帧头检测:
find_frame_header
函数逐字节比对帧头模式- 使用
uint8_t
类型转换避免符号扩展问题 - 精确匹配连续3字节的帧头序列
- 粘包处理:
- 当未找到完整帧头时,保留最后2字节(
HEADER_SIZE - 1
)防止丢弃可能的帧头前缀 - 找到帧头后检查剩余数据是否满足完整数据包长度
- 当未找到完整帧头时,保留最后2字节(
- 数据包处理:
- 检测到完整数据包后调用
process_packet
处理 - 每次处理后从缓冲区移除已处理数据(包含帧头)
- 检测到完整数据包后调用
- 异常处理:
- 检测客户端断开连接(
recv
返回0) - 基础错误检查(socket创建、绑定、监听等)
- 检测客户端断开连接(
使用注意事项:
- 编译时需要链接网络库(Linux下添加
-lpthread
参数) - 实际使用时需完善以下部分:
process_packet
函数中的业务逻辑- 添加校验和验证(如协议需要)
- 多客户端支持(可通过多线程/select/epoll实现)
- 错误日志记录
- 性能优化(环形缓冲区、零拷贝等)
该实现能够有效处理TCP粘包问题,确保完整数据包的接收和处理,适用于需要固定长度数据包传输的场景。