前言:欢迎 各位光临本博客,这里IFMAXUE带你直接手撕,文章并不复杂,愿诸君**耐其心性,忘却杂尘,道有所长!!!!

IF'Maxue :个人主页
🔥 个人专栏 :
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
⛺️生活是默默的坚持,毅力是永久的享受。不破不立!
文章目录
-
- 一、开篇:五层协议与应用层核心痛点
- 二、核心解决方案:序列化与反序列化
-
- [1. 先否定两种不推荐的方案](#1. 先否定两种不推荐的方案)
- [2. 序列化与反序列化的通俗理解](#2. 序列化与反序列化的通俗理解)
- 三、底层原理:Socket读写接口的本质
-
- [1. `write/send`的本质](#1.
write/send的本质) - [2. `read`的本质](#2.
read的本质) - [3. TCP通信的终极本质](#3. TCP通信的终极本质)
- [1. `write/send`的本质](#1.
- 四、实战封装:模板方法模式封装Socket(TCP/UDP)
-
- [1. 父类Socket封装(Socket.cpp)](#1. 父类Socket封装(Socket.cpp))
- [2. 辅助类inetAddr:封装网络地址](#2. 辅助类inetAddr:封装网络地址)
- [3. TCP子类:重写差异化方法](#3. TCP子类:重写差异化方法)
- [4. TCPServer封装:实现TCP长服务](#4. TCPServer封装:实现TCP长服务)
- [5. UDP子类:重写差异化方法](#5. UDP子类:重写差异化方法)
- [6. 主函数入口:启动TCP服务端](#6. 主函数入口:启动TCP服务端)
- 五、核心实战:应用层协议定制(网络计算器)
-
- [1. 协议的核心职责](#1. 协议的核心职责)
- [2. 定义结构化数据模型](#2. 定义结构化数据模型)
- [3. 协议层封装:衔接Socket与业务逻辑](#3. 协议层封装:衔接Socket与业务逻辑)
- [4. 整合协议层与服务端:处理计算器业务](#4. 整合协议层与服务端:处理计算器业务)
- [5. 整体架构:三层解耦清晰高效](#5. 整体架构:三层解耦清晰高效)
- 六、优化方案:使用jsoncpp实现序列化与反序列化
-
- [1. 序列化方案对比与选择](#1. 序列化方案对比与选择)
- [2. 环境检查:确认jsoncpp是否安装](#2. 环境检查:确认jsoncpp是否安装)
- [3. jsoncpp核心使用:序列化(结构化数据→JSON字符串)](#3. jsoncpp核心使用:序列化(结构化数据→JSON字符串))
- [4. jsoncpp核心使用:反序列化(JSON字符串→结构化数据)](#4. jsoncpp核心使用:反序列化(JSON字符串→结构化数据))
- [5. FastWriter与StyledWriter的区别](#5. FastWriter与StyledWriter的区别)
- [6. 匿名表达式(Lambda)的使用](#6. 匿名表达式(Lambda)的使用)
一、开篇:五层协议与应用层核心痛点
我们在之前的学习中编写的所有Socket相关代码,都处于五层网络协议的应用层,这是我们开发者接触最多、能够自主定制逻辑的层级。

在实际开发中,我们很快会遇到一个核心问题:Socket提供的API接口,在读写网络数据时,默认只支持「纯字符串」格式的收发。但我们的业务逻辑中,往往需要传输结构化数据(比如计算器的两个操作数+一个运算符),这种情况下该如何处理呢?

二、核心解决方案:序列化与反序列化
要解决结构化数据的网络传输问题,核心就是序列化与反序列化,这也是应用层网络通信的最佳实践。


1. 先否定两种不推荐的方案
首先要明确,有两种方案可以实现结构化数据传输,但非常不推荐:
- 逐个字段读写发送:比如先发送操作数1,再发送运算符,最后发送操作数2,接收方按相同顺序逐个读取。这种方式不仅效率极低,而且耦合度极高,一旦字段顺序变更或新增字段,两端代码都要修改,容易出现逻辑错误。
- 直接传输结构体:在C/C++中直接将结构体通过Socket发送,这种方式虽然比逐个字段发送高效,但存在跨平台兼容性问题(不同系统的字节序、结构体对齐规则不同),而且扩展性差,仅适用于极少数封闭、无跨平台需求的特殊场景。

2. 序列化与反序列化的通俗理解
- 序列化:把本地的结构化数据(比如C++的类、结构体),转换成一段可以在网络中传输的「纯字符串」或「二进制流」,相当于给结构化数据打了一个「包裹」,方便网络传输。
- 反序列化:把从网络中接收的「字符串/二进制流」,还原成本地可以识别和处理的结构化数据,相当于把传输过来的「包裹」拆开,提取里面的有效信息。
这一过程完美解决了结构化数据的跨主机传输问题,也是应用层协议设计的基础。
三、底层原理:Socket读写接口的本质
在深入封装和实战之前,我们需要先打破一个常见误区:write/send和read是不是直接把数据发送到网络、从网络读取数据?

答案是否定的,这些接口的本质都是**「内存拷贝」**,数据的实际网络传输由操作系统和TCP协议负责,具体细节如下:
1. write/send的本质
当我们调用write或send接口发送数据时,函数执行的操作仅仅是:把用户态缓冲区中的数据,拷贝到内核态的TCP发送缓冲区中。
只要拷贝完成,这个函数就会返回,至于数据什么时候被发送到网络、发送过程中出现丢包该如何重传、网络拥堵时该如何控制发送速率,这些都由操作系统和TCP协议自主决定,我们开发者无需进行任何干预,这也是TCP被称为「传输控制协议」的核心原因。
简单示例代码(TCP发送数据):
cpp
// 用户态缓冲区(结构化数据转换后的字符串)
std::string send_data = "10 + 20";
// sockfd是已建立连接的Socket文件描述符
ssize_t ret = write(sockfd, send_data.c_str(), send_data.size());
if (ret < 0) {
perror("write error");
return -1;
}
// 此时仅完成用户态到内核发送缓冲区的拷贝,并非已发送到网络
2. read的本质
当我们调用read接口接收数据时,函数执行的操作仅仅是:把内核态的TCP接收缓冲区中的数据,拷贝到用户态缓冲区中。
如果TCP接收缓冲区中没有可用数据,read接口会进入阻塞状态(这是同步IO模型的特性),直到有数据到达并完成拷贝后才会返回。
简单示例代码(TCP接收数据):
cpp
// 用户态缓冲区
char recv_buf[1024] = {0};
// sockfd是已建立连接的Socket文件描述符
ssize_t ret = read(sockfd, recv_buf, sizeof(recv_buf) - 1);
if (ret < 0) {
perror("read error");
return -1;
}
// 此时仅完成内核接收缓冲区到用户态的拷贝,数据来自对端的内核发送缓冲区
3. TCP通信的终极本质
两台主机之间的TCP通信,本质上是一个「缓冲区拷贝+网络传输」的流程:
发送方用户态缓冲区 → 发送方内核TCP发送缓冲区 → 网络传输 → 接收方内核TCP接收缓冲区 → 接收方用户态缓冲区。
我们开发者接触到的,仅仅是两端的「用户态<->内核态」拷贝,中间的网络传输细节被TCP协议和操作系统封装得严严实实,这也让我们的开发变得更加简洁高效。
四、实战封装:模板方法模式封装Socket(TCP/UDP)
为了实现「底层网络操作」和「上层业务逻辑」的解耦,我们采用模板方法模式来封装Socket,核心思路是:提取TCP和UDP通信的公共流程作为父类模板,差异化实现由子类重写。
1. 父类Socket封装(Socket.cpp)
首先定义Socket父类,封装所有Socket通信的公共逻辑(比如创建套接字、关闭套接字等),并定义纯虚函数作为差异化接口,由子类实现。

核心代码示例(Socket.hpp):
cpp
#pragma once
#include <sys/socket.h>
#include <unistd.h>
class Socket {
public:
// 构造函数:创建套接字
Socket(int domain, int type, int protocol) {
_sockfd = socket(domain, type, protocol);
if (_sockfd < 0) {
perror("socket create error");
exit(EXIT_FAILURE);
}
}
// 析构函数:关闭套接字
virtual ~Socket() {
if (_sockfd >= 0) {
close(_sockfd);
_sockfd = -1;
}
}
// 获取Socket文件描述符
int getSockfd() const { return _sockfd; }
// 纯虚函数:绑定地址(公共流程,子类可重写细化)
virtual int bind(const struct sockaddr* addr, socklen_t addrlen) = 0;
// 纯虚函数:接收连接(仅TCP需要,UDP无需实现)
virtual int accept(struct sockaddr* addr, socklen_t* addrlen) = 0;
private:
int _sockfd; // Socket文件描述符
};
2. 辅助类inetAddr:封装网络地址
实现inetAddr类,提供setaddr方法,封装struct sockaddr_in地址结构,简化IP地址和端口号的配置与转换,避免直接操作繁琐的C语言地址结构。

核心代码示例(inetAddr.hpp):
cpp
#pragma once
#include <netinet/in.h>
#include <string>
class inetAddr {
public:
// 构造函数:初始化地址结构
inetAddr() : _addr({0}) {}
// setaddr方法:配置IP和端口
void setaddr(const std::string& ip, uint16_t port) {
_addr.sin_family = AF_INET; // IPv4协议
// 转换IP地址(字符串→网络字节序)
inet_pton(AF_INET, ip.c_str(), &_addr.sin_addr);
// 转换端口号(主机字节序→网络字节序)
_addr.sin_port = htons(port);
}
// 获取封装后的sockaddr结构
const struct sockaddr_in& getAddr() const { return _addr; }
private:
struct sockaddr_in _addr; // IPv4地址结构
};
3. TCP子类:重写差异化方法
TCP协议有连接特性,需要实现listen(监听端口)、accept(接收连接)等特有方法,子类继承自Socket父类,重写纯虚函数,实现TCP通信的差异化逻辑。


核心代码示例(TCPSocket.hpp):
cpp
#pragma once
#include "Socket.hpp"
#include "inetAddr.hpp"
class TCPSocket : public Socket {
public:
// 构造函数:创建TCP套接字(SOCK_STREAM:流式套接字)
TCPSocket() : Socket(AF_INET, SOCK_STREAM, 0) {}
// 重写bind方法:绑定IP和端口
int bind(const struct sockaddr* addr, socklen_t addrlen) override {
return ::bind(getSockfd(), addr, addrlen);
}
// 重写accept方法:接收客户端连接
int accept(struct sockaddr* addr, socklen_t* addrlen) override {
return ::accept(getSockfd(), addr, addrlen);
}
// TCP特有方法:监听端口
int listen(int backlog) {
return ::listen(getSockfd(), backlog);
}
};
4. TCPServer封装:实现TCP长服务
封装TCPServer类,整合TCPSocket和inetAddr,实现TCP服务端的完整流程(初始化、监听、接收连接),让服务端开发更加简洁,且无需关心底层Socket细节。




核心代码示例(TCPServer.hpp):
cpp
#pragma once
#include "TCPSocket.hpp"
#include "inetAddr.hpp"
#include <functional>
#include <thread>
class TCPServer {
public:
// 构造函数:初始化服务端IP和端口
TCPServer(const std::string& ip, uint16_t port) : _ip(ip), _port(port), _tcp_socket() {}
// 初始化服务端:绑定+监听
bool init() {
inetAddr addr;
addr.setaddr(_ip, _port);
// 绑定地址
if (_tcp_socket.bind((const struct sockaddr*)&addr.getAddr(), sizeof(addr.getAddr())) < 0) {
return false;
}
// 监听端口(backlog设为10)
if (_tcp_socket.listen(10) < 0) {
return false;
}
return true;
}
// 启动服务端:循环接收客户端连接
void start(std::function<void(int)> handle_client) {
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
while (true) {
// 接收客户端连接
int client_fd = _tcp_socket.accept((struct sockaddr*)&client_addr, &client_addr_len);
if (client_fd < 0) {
perror("accept error");
continue;
}
// 开启子线程处理客户端请求(避免阻塞主线程)
std::thread t(handle_client, client_fd);
t.detach(); // 分离线程,自动回收资源
}
}
private:
std::string _ip; // 服务端IP
uint16_t _port; // 服务端端口
TCPSocket _tcp_socket; // TCP Socket对象
};
5. UDP子类:重写差异化方法
UDP协议无连接特性,无需listen和accept方法,使用recvfrom和sendto收发数据,子类同样继承自Socket父类,实现UDP通信的差异化逻辑。

核心代码示例(UDPSocket.hpp):
cpp
#pragma once
#include "Socket.hpp"
class UDPSocket : public Socket {
public:
// 构造函数:创建UDP套接字(SOCK_DGRAM:数据报套接字)
UDPSocket() : Socket(AF_INET, SOCK_DGRAM, 0) {}
// 重写bind方法:绑定IP和端口(UDP也需要绑定端口接收数据)
int bind(const struct sockaddr* addr, socklen_t addrlen) override {
return ::bind(getSockfd(), addr, addrlen);
}
// 重写accept方法:UDP无需接收连接,直接返回-1(标识不支持)
int accept(struct sockaddr* addr, socklen_t* addrlen) override {
return -1;
}
// UDP特有方法:发送数据到指定地址
ssize_t sendto(const void* buf, size_t len, const struct sockaddr* dest_addr, socklen_t addrlen) {
return ::sendto(getSockfd(), buf, len, 0, dest_addr, addrlen);
}
// UDP特有方法:从指定地址接收数据
ssize_t recvfrom(void* buf, size_t len, struct sockaddr* src_addr, socklen_t* addrlen) {
return ::recvfrom(getSockfd(), buf, len, 0, src_addr, addrlen);
}
};
6. 主函数入口:启动TCP服务端
通过main.cc整合上述封装,启动TCP服务端,指定处理客户端请求的回调函数,完成底层网络服务的搭建。

核心代码示例(main.cc):
cpp
#include "TCPServer.hpp"
#include <iostream>
// 客户端请求处理函数(后续将与协议层联动)
void handle_client(int client_fd) {
std::cout << "新客户端连接:fd = " << client_fd << std::endl;
// 后续将在这里通过Protocol解析请求、处理业务、返回响应
close(client_fd);
}
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "使用方式:./tcpserver [IP] [Port]" << std::endl;
return 1;
}
std::string ip = argv[1];
uint16_t port = std::stoi(argv[2]);
// 创建TCP服务端对象
TCPServer server(ip, port);
// 初始化服务端
if (!server.init()) {
std::cerr << "服务端初始化失败" << std::endl;
return 1;
}
std::cout << "TCP服务端启动成功,监听:" << ip << ":" << port << std::endl;
// 启动服务端,传入客户端处理函数
server.start(handle_client);
return 0;
}
五、核心实战:应用层协议定制(网络计算器)
底层Socket封装完成后,我们需要定制应用层协议,约定结构化数据的收发规则,这里以「网络计算器」为例,实现请求与响应的结构化传输。
1. 协议的核心职责
应用层协议就像通信双方的「约定手册」,核心职责是明确「如何读」和「如何写」,即如何将结构化的请求/响应转换为可传输的数据,以及如何从传输的数据中还原结构化信息。

2. 定义结构化数据模型
首先定义计算器的请求体(Request)和响应体(Response),对应结构化数据,包含业务所需的核心字段:
Request:两个操作数(x、y)、一个运算符(op);Response:计算结果(ret)、退出码(code,标识计算是否成功,比如0=成功、1=除零错误、2=非法运算符)。


核心代码示例(Protocol.hpp 数据模型定义):
cpp
#pragma once
#include <string>
// 计算器请求体:结构化数据
struct Request {
int x; // 操作数1
int y; // 操作数2
char op; // 运算符(+、-、*、/)
};
// 计算器响应体:结构化数据
struct Response {
int ret; // 计算结果
int code; // 退出码:0=成功,1=除零错误,2=非法运算符
};
3. 协议层封装:衔接Socket与业务逻辑
封装Protocol类,实现「序列化」和「反序列化」方法,衔接底层Socket和上层计算器业务,核心功能是:
- 将
Request序列化为可传输的字符串; - 将接收的字符串反序列化为
Request; - 将
Response序列化为可传输的字符串; - 将接收的字符串反序列化为
Response; - 接收Socket文件描述符,实现网络数据的读写与解析联动。




核心代码示例(Protocol.hpp 协议方法封装):
cpp
// 接续上面的数据模型定义
#include <unistd.h>
#include <cstring>
class Protocol {
public:
// 构造函数:传入Socket文件描述符
Protocol(int sockfd) : _sockfd(sockfd) {}
// 1. Request序列化:结构化数据→字符串(先采用自定义分隔符方案)
bool serializeRequest(const Request& req, std::string& out_str) {
// 拼接格式:x op y(用空格分隔)
out_str = std::to_string(req.x) + " " + req.op + " " + std::to_string(req.y);
return true;
}
// 2. 字符串反序列化→Request
bool deserializeRequest(const std::string& in_str, Request& req) {
// 分割字符串:按空格提取字段
size_t pos1 = in_str.find(" ");
size_t pos2 = in_str.find(" ", pos1 + 1);
if (pos1 == std::string::npos || pos2 == std::string::npos) {
return false;
}
// 提取并转换字段
req.x = std::stoi(in_str.substr(0, pos1));
req.op = in_str[pos1 + 1];
req.y = std::stoi(in_str.substr(pos2 + 1));
return true;
}
// 3. Response序列化:结构化数据→字符串
bool serializeResponse(const Response& resp, std::string& out_str) {
// 拼接格式:ret code(用空格分隔)
out_str = std::to_string(resp.ret) + " " + std::to_string(resp.code);
return true;
}
// 4. 字符串反序列化→Response
bool deserializeResponse(const std::string& in_str, Response& resp) {
// 分割字符串:按空格提取字段
size_t pos = in_str.find(" ");
if (pos == std::string::npos) {
return false;
}
// 提取并转换字段
resp.ret = std::stoi(in_str.substr(0, pos));
resp.code = std::stoi(in_str.substr(pos + 1));
return true;
}
// 5. 发送数据(通过Socket写入序列化后的字符串)
bool sendData(const std::string& data) {
ssize_t ret = write(_sockfd, data.c_str(), data.size());
return ret == (ssize_t)data.size();
}
// 6. 接收数据(通过Socket读取字符串到用户缓冲区)
bool recvData(std::string& data, size_t buf_size = 1024) {
char buf[1024] = {0};
ssize_t ret = read(_sockfd, buf, buf_size - 1);
if (ret <= 0) {
return false;
}
data = std::string(buf, ret);
return true;
}
private:
int _sockfd; // Socket文件描述符,用于网络读写
};
4. 整合协议层与服务端:处理计算器业务
修改main.cc中的handle_client函数,整合Protocol类,实现「接收请求→解析→计算→返回响应」的完整业务流程。



修改后的handle_client函数:
cpp
#include "Protocol.hpp"
#include <iostream>
// 计算器核心业务逻辑:执行计算
void calculate(const Request& req, Response& resp) {
resp.code = 0; // 默认成功
switch (req.op) {
case '+':
resp.ret = req.x + req.y;
break;
case '-':
resp.ret = req.x - req.y;
break;
case '*':
resp.ret = req.x * req.y;
break;
case '/':
if (req.y == 0) {
resp.code = 1; // 除零错误
resp.ret = 0;
} else {
resp.ret = req.x / req.y;
}
break;
default:
resp.code = 2; // 非法运算符
resp.ret = 0;
break;
}
}
// 客户端请求处理函数:整合协议层与业务逻辑
void handle_client(int client_fd) {
std::cout << "新客户端连接:fd = " << client_fd << std::endl;
// 创建协议对象,关联Socket文件描述符
Protocol protocol(client_fd);
Request req;
Response resp;
std::string recv_str, send_str;
// 接收并解析请求
if (!protocol.recvData(recv_str)) {
std::cerr << "接收请求失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
if (!protocol.deserializeRequest(recv_str, req)) {
std::cerr << "解析请求失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
// 执行计算
calculate(req, resp);
// 序列化并发送响应
if (!protocol.serializeResponse(resp, send_str)) {
std::cerr << "序列化响应失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
if (!protocol.sendData(send_str)) {
std::cerr << "发送响应失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
std::cout << "客户端请求处理完成,fd = " << client_fd << std::endl;
close(client_fd);
}
5. 整体架构:三层解耦清晰高效
整个网络计算器的架构分为三层,各司其职,实现了完美解耦,从网络通信直接过渡到业务处理,无需关心底层细节。


同时,基于TCP的应用层协议需要解决两个核心问题(后续优化重点):
- 粘包问题:TCP是流式协议,多个请求/响应可能会粘在一起传输,需要约定分隔符或长度字段来区分;
- 完整数据解析问题:确保读取到一个完整的结构化数据,避免读取到不完整的片段导致解析失败。

六、优化方案:使用jsoncpp实现序列化与反序列化
我们之前采用了「自定义分隔符」的序列化方案,虽然实现简单,但可扩展性差,面对复杂结构化数据时容易出错。实战中,我们优先使用成熟的开源库,这里以jsoncpp为例,实现更高效、更稳定的序列化与反序列化。
1. 序列化方案对比与选择
序列化方案主要分为两类:
- 自定义实现:如分隔符、二进制流等,适合简单场景,无需依赖第三方库;
- 成熟开源库:如jsoncpp(JSON格式)、protobuf(二进制格式)等,适合复杂场景,可扩展性强,兼容性好。

2. 环境检查:确认jsoncpp是否安装
首先通过终端命令检查系统是否安装了jsoncpp,确保开发环境可用:
bash
pkg-config --list-all | grep json

3. jsoncpp核心使用:序列化(结构化数据→JSON字符串)
jsoncpp将结构化数据转换为JSON格式的字符串,方便网络传输,核心步骤是:
- 引入jsoncpp头文件(注意带路径,
#include <json/json.h>); - 创建
Json::Value对象,存入结构化数据的字段; - 使用
Json::Writer(FastWriter或StyledWriter)将Json::Value转换为字符串。



核心代码示例(Protocol.hpp 中修改序列化方法):
cpp
// 引入jsoncpp头文件
#include <json/json.h>
// 重写Request序列化:使用jsoncpp(结构化数据→JSON字符串)
bool serializeRequest(const Request& req, std::string& out_str) {
Json::Value root;
root["x"] = req.x;
root["y"] = req.y;
root["op"] = req.op;
// FastWriter:生成紧凑格式JSON字符串(无缩进,适合网络传输)
Json::FastWriter writer;
out_str = writer.write(root);
return true;
}
// 重写Response序列化:使用jsoncpp(结构化数据→JSON字符串)
bool serializeResponse(const Response& resp, std::string& out_str) {
Json::Value root;
root["ret"] = resp.ret;
root["code"] = resp.code;
Json::FastWriter writer;
out_str = writer.write(root);
return true;
}
序列化后的结果是一段标准的JSON字符串,带有换行符,可直接通过Socket传输,示例如下:
json
{"x":10,"y":20,"op":"+"}\n


4. jsoncpp核心使用:反序列化(JSON字符串→结构化数据)
反序列化是序列化的逆过程,核心步骤是:
- 引入jsoncpp头文件;
- 创建
Json::Reader对象,将接收的JSON字符串解析为Json::Value对象; - 从
Json::Value对象中提取字段,赋值给结构化数据。
核心代码示例(Protocol.hpp 中修改反序列化方法):
cpp
// 重写Request反序列化:使用jsoncpp(JSON字符串→结构化数据)
bool deserializeRequest(const std::string& in_str, Request& req) {
Json::Value root;
Json::Reader reader;
// 解析JSON字符串
if (!reader.parse(in_str, root)) {
return false;
}
// 提取字段并赋值
req.x = root["x"].asInt();
req.y = root["y"].asInt();
req.op = root["op"].asChar();
return true;
}
// 重写Response反序列化:使用jsoncpp(JSON字符串→结构化数据)
bool deserializeResponse(const std::string& in_str, Response& resp) {
Json::Value root;
Json::Reader reader;
// 解析JSON字符串
if (!reader.parse(in_str, root)) {
return false;
}
// 提取字段并赋值
resp.ret = root["ret"].asInt();
resp.code = root["code"].asInt();
return true;
}
5. FastWriter与StyledWriter的区别
jsoncpp提供了两种Writer,适用于不同场景:
FastWriter:生成紧凑格式 的JSON字符串,无缩进、无多余空格,体积更小,适合网络传输 和数据存储;StyledWriter:生成格式化格式 的JSON字符串,有缩进、有换行,可读性更强,适合调试和日志输出。


6. 匿名表达式(Lambda)的使用
在实际开发中,我们常使用Lambda表达式简化代码,比如在TCPServer的start方法中,直接传入Lambda表达式作为客户端处理函数,无需单独定义函数,让代码更简洁紧凑。

示例代码(main.cc 中简化启动逻辑):
cpp
// 启动服务端,直接传入Lambda表达式作为客户端处理函数
server.start([](int client_fd) {
std::cout << "新客户端连接:fd = " << client_fd << std::endl;
// 内部逻辑与之前的handle_client函数一致
Protocol protocol(client_fd);
Request req;
Response resp;
std::string recv_str, send_str;
if (!protocol.recvData(recv_str) || !protocol.deserializeRequest(recv_str, req)) {
std::cerr << "处理请求失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
calculate(req, resp);
if (!protocol.serializeResponse(resp, send_str) || !protocol.sendData(send_str)) {
std::cerr << "发送响应失败,fd = " << client_fd << std::endl;
close(client_fd);
return;
}
std::cout << "客户端请求处理完成,fd = " << client_fd << std::endl;
close(client_fd);
});