【Linux】序列化与反序列化——网络计算器的实现

发布时间:2026/5/26 12:02:40

【Linux】序列化与反序列化——网络计算器的实现 从 TCP 字节流困境到结构化通信为什么我们需要序列化与反序列化有了套接字编程的基础我们便能快速实现简单的服务端与客户端搭建完成最基本的数据传输。但在实际网络通信中一个无法回避的问题会随之浮现TCP 是面向字节流的传输协议。当对端调用read读取数据时受缓冲区大小限制、发送端数据发送缓慢、网络波动、丢包 / 粘包等多种因素影响很容易出现读取数据不完整或是一次读到多条消息、一条消息被拆分成多次读取的情况。字节流本身没有天然的 “消息边界”这会直接导致接收方解析出错误数据通信逻辑彻底紊乱。因此在网络传输中明确消息边界、规范数据格式就成了保障通信稳定的核心需求。更进一步思考即便我们通过长度报头等方式解决了消息边界问题数据传输依然面临另一重巨大风险 ——跨平台、跨环境的兼容性问题。发送端与接收端可能运行在不同 CPU 架构、不同操作系统上存在大小端字节序差异、内存对齐规则不同、数据类型长度不一致等问题。如果直接传输内存中的原生结构体、哈希表等对象多字节数值会因字节序解析错误指针会在跨进程后完全失效甚至内存布局差异都会导致数据彻底乱掉。与此同时如果收发的只是简单文本用户尚可直接阅读并回复但当服务端与客户端需要进行功能性交互、指令调度、结构化数据传递时通信就不再是 “让人看懂” 那么简单 —— 程序必须能精准识别数据含义、解析字段含义、执行对应的业务逻辑。单纯的字符串或无格式字节流既无法高效承载复杂结构也不具备统一的解析标准难以支撑起稳定、可扩展的网络交互。正是为了解决消息边界、结构化数据传输、屏蔽平台环境差异这三大核心问题我们需要一套机制能够将内存中的对象、结构体、复杂数据转换成与平台无关、网络中可安全传输的标准字节流也能将接收到的字节流还原成程序可识别的数据结构。而这套机制正是我们今天要深入讲解的 —— 序列化与反序列化。从问题到方案序列化 / 反序列化如何破解通信难题简单来说序列化Serialization是将程序中结构化的数据比如类实例、字典、结构体转换成连续的字节流或字符串的过程而反序列化Deserialization则是其逆过程 —— 把接收到的字节流还原为程序可直接操作的结构化数据。但是这里所谓的连续字节流、字符串也并不是简单的对字符串进行拼接比如网络登录服务客户端需要传递 “用户名 密码 登录类型” 三组数据若直接拼接成字符串user1:123456:phone发送一旦某字段包含特殊字符如冒号就会解析出错而且字符串并没有标记数据边界很容易出现对端收到的数据不完整导致出错的问题因此需要解决的问题就有如何保证对端精确提取数据如何保证对方正确的拿到完整信息如何正确进行序列化-需要注意哪些问题针对上面的登录案例出现的两个问题我们进行一一分析如何保证对方正确的拿到完整信息这个问题严格来说并不是序列化以及反序列化问题的关键但是与序列化反序列化问题之间有强耦合——只有保证数据完整性才能正确进行序列化和反序列化处理。想要处理这个问题其实很简单只需要在传输报文前面加上长度信息报头比如需要传输的报文为usr1:123456:phone我们就可以将其改为17\n usr1:123456:phone,这样当对端进行read读取的时候就可以先拿到长度报头随后继续进行read当接下来read到的数据长度不够时就返回去继续read直到读到完整数据当读取数据长度超出报头数据的时候也可以根据报头信息进行截取。如何保证对端精确提取数据解决了第一个问题那么如何保证对端能够精确地提取到所需数据呢若将业务数据拼接为usr1:123456:phone这类分隔符格式传输反序列化时虽可通过分号:拆分出 “用户名、密码、登录类型”但这种方式存在致命缺陷 ——数据完全依赖固定顺序一旦发送端调整字段顺序比如改为phone:usr1:123456接收端按原有顺序解析会直接得到错误数据把 “phone” 解析为用户名“usr1” 解析为密码甚至当字段缺失 / 新增时整个解析逻辑会彻底失效。此时就需要更为精确的方法——键值对标记那么就很容易想到结构体、哈希表如unordered_map等做法但是这里明显忽略了一个问题不同CPU架构存在大小端差异小端低字节数据存放在内存低地址高字节数据存放在内存高地址大端高字节数据存放在内存低地址低字节数据存放在内存高地址若报文以原生二进制形式包含 int/longlong 等多字节变量且未统一字节序会因大小端差异导致对端解析错误而 unordered_map 等数据结构因内部包含指针直接传输会导致指针跨进程失效甚至引发内存越界崩溃。Json串引入但是JSON的引入很好地解决了这个问题——JSON 是一种轻量级的纯文本结构化数据交换格式它以简单的键值对结构描述数据不依赖任何操作系统、CPU 架构与编程语言天然具备极强的跨平台性。由于采用单字节字符存储数据JSON 从根本上避开了大小端字节序、内存对齐、指针等底层差异问题无论在 Windows、Linux 还是嵌入式设备或是 x86、ARM 等不同硬件平台上都能被一致解析、准确还原因此成为网络通信中最通用的序列化方案。一、核心数据表示JSON 结构构建JSON 的核心是键值对支持字符串、数字、布尔、数组、嵌套对象等类型通过Json::Value构建#include iostream #include fstream #include json/json.h // 引入JsonCpp头文件需提前安装/链接 int main() { // 1. 构建基础键值对JSON对象 Json::Value root; root[name] 张三; // 字符串值 root[age] 25; // 数字值 root[is_student] false; // 布尔值 root[score] Json::nullValue; // 空值 // 2. 构建数组类型 root[hobbies] Json::Value(Json::arrayValue); // 初始化数组 root[hobbies].append(编程); root[hobbies].append(阅读); // 3. 构建嵌套对象 Json::Value info; info[phone] 12345678901; info[addr] 北京市海淀区; root[user_info] info; // 嵌套到根对象二、序列化内存 JSON 对象→JSON 字符串 / 流将Json::Value对象转为可传输 / 存储的 JSON 格式核心有 3 种写入方式#includejsoncpp/json/json.h #includeiostream int main(){ Json::Value root; root[name]zhangsan; root[age]14; // 格式化写入带缩进易读适合调试/日志 Json::StyledWriter _sw; // 快速写入紧凑格式无缩进适合网络传输 Json::FastWriter _fw; // 直接写入流文件/网络流低内存占用适合大数据 Json::StyledStreamWriter _ssw; std::string SJ_str _sw.write(root); std::string FJ_str _fw.write(root); std::coutStyledStreamWriter\n; _ssw.write(std::cout,root); std::coutstd::endlFastWriter\nFJ_strstd::endlStyledWriter\nSJ_str; }运行效果./a.out StyledStreamWriter { age : 14, name : zhangsan } FastWriter {age:14,name:zhangsan} StyledWriter { age : 14, name : zhangsan }三、反序列化JSON 字符串 / 流→内存 JSON 对象将接收到 / 读取的 JSON 文本还原为程序可操作的Json::Value对象// 1. 从字符串反序列化 std::string jsonStr R({name:张三,age:25,hobbies:[编程,阅读]}); Json::Value parseRoot; Json::Reader reader; if (reader.parse(jsonStr, parseRoot)) { // 解析字符串 std::cout 【字符串反序列化结果】\n; std::cout 姓名 parseRoot[name].asString() \n; std::cout 年龄 parseRoot[age].asInt() \n; std::cout 第一个爱好 parseRoot[hobbies][0].asString() \n\n; } // 2. 从文件流反序列化 std::ifstream readFile(user.json); Json::Value fileRoot; if (reader.parse(readFile, fileRoot)) { // 解析文件流 std::cout 【文件反序列化结果】\n; std::cout 手机号 fileRoot[user_info][phone].asString() \n; } readFile.close(); return 0; }总的来说JSON 的操作流程可清晰划分为三个关键环节首先构建符合业务逻辑的 JSON 对象其次将该对象序列化为可在网络传输、本地存储的文本格式最后把接收到的 JSON 文本反序列化还原成程序能识别和操作的对象这三步恰好对应了序列化与反序列化的核心步骤。这里具体到我们的网络计算器部分就可以构建request和response对象用JSON进行序列化和反序列化class Request { public: Request() { } Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper) { } std::string Serialization() { Json::Value root; root[X] _x; root[Y] _y; root[OPER] _oper; Json::FastWriter writer; std::string s writer.write(root); return s; } bool Deserialization(std::string in) { Json::Value root; Json::Reader reader; bool ok reader.parse(in, root); if (ok) { _x root[X].asInt(); _y root[Y].asInt(); _oper root[OPER].asInt(); } return ok; } }class Response { public: Response() { } Response(int result, int code) : _code(code), _result(result) { } std::string Serialization() { Json::Value root; root[CODE] _code; root[RESULT] _result; Json::FastWriter writer; std::string s writer.write(root); return s; } bool Deserialization(std::string in) { Json::Value root; Json::Reader reader; bool ok reader.parse(in, root); if (ok) { _code root[CODE].asInt(); _result root[RESULT].asInt(); } return ok; } }这样一来我们client端向server端进行数据请求的时候就可以把操作数、操作符打包成JSON风格的字符串再加上长度报头server端拿到字符串就可以根据报头信息截取对应长度信息随后提取字符串中的操作数、操作符信息随后进行结果计算然后将计算结果进行打包返回给client端实现双端‘无障碍‘沟通。网络计算器的实现——从分布到整体该网络计算器项目采用分层解耦的设计思路通过头文件划分核心功能模块将整体逻辑拆分为三个独立且低耦合的层既保证了代码的可维护性也便于后续扩展一、分层设计核心思路项目核心拆分为三大模块各模块通过头文件Socket.hpp/NetCal.hpp/Protocol.hpp/Tcpserver.hpp封装自身逻辑仅对外暴露必要接口实现 “各司其职、按需协作”服务器层TcpServer Socket负责底层网络通信的核心能力基于Socket封装套接字操作创建、绑定、监听、连接通过TcpServer实现 TCP 服务端的启动、客户端连接管理是整个项目的 “通信基座”。代码中通过std::unique_ptrTcpServer创建服务器实例传入端口号并调用Run()启动同时将协议层的处理逻辑作为回调注入实现 “通信层” 与 “业务层” 的解耦。计算核心层NetCal.hpp 中的 Cal 类聚焦业务逻辑本身封装计算器的核心计算能力通过Cal::Execute(req)方法处理具体的计算请求。该层完全脱离网络细节仅接收标准化的Request请求、返回Response响应是项目的 “业务核心”。代码中通过std::unique_ptrCal创建计算实例仅作为回调参数传递给协议层保证计算逻辑的独立性 —— 即使替换通信方式如 UDP计算层代码也无需修改。协议层Protocol.hpp 中的 Protocol 类作为 “通信层” 与 “计算层” 的桥梁负责请求 / 响应的序列化、反序列化、数据解析与封装。代码中Protocol类通过回调绑定Cal的计算方法同时通过Getrequest(sock, addr)方法从套接字读取数据、解析为Request对象调用计算逻辑后再将Response封装为网络可传输格式屏蔽了底层数据格式与通信细节的耦合。二、整体执行流程对应代码逻辑程序启动时校验端口参数初始化计算核心Cal、协议层Protocol绑定计算回调、TCP 服务器TcpServer绑定协议层处理回调调用server-Run()启动 TCP 服务器监听指定端口并等待客户端连接当客户端发起连接时服务器层触发协议层的Getrequest方法从套接字读取数据协议层解析数据为Request对象调用Cal::Execute完成计算得到Response后封装为网络格式回传给客户端整个流程中各层通过回调函数协作无直接强依赖实现了 “网络通信 - 协议解析 - 业务计算” 的分层解耦。三、设计优势低耦合各层仅通过接口交互修改任意一层如将 JSON 协议替换为 Protobuf、将 TCP 改为 UDP、扩展计算功能无需改动其他层代码高复用Cal类可脱离网络场景单独复用如本地计算器TcpServer可适配其他业务场景如聊天服务器易维护问题定位更精准网络问题查服务器层、计算错误查业务层、格式问题查协议层便于迭代升级。#include Socket.hpp #include iostream #include memory #include NetCal.hpp #include Tcpserver.hpp #include Protocol.hpp using namespace SockModule; int main(int argc, char *argv[]) { if (argc ! 2) { std::cout Usage argv[0] : _port; } std::unique_ptrCal cal std::make_uniqueCal(); // 核心解耦体现通过回调函数将各层串联而非直接依赖 // 1. 协议层绑定计算层业务逻辑注入 std::unique_ptrProtocol protocol std::make_uniqueProtocol([cal](Request req)-Response { return cal-Execute(req); }); // 2. 服务器层绑定协议层通信逻辑注入 std::unique_ptrTcpServer server std::make_uniqueTcpServer([protocol](std::shared_ptrSocket sock, InetAddr addr) { protocol-Getrequest(sock, addr); }, std::stoi(argv[1])); server-Run(); }Tcpserver.hpp 服务器构建#pragma once #include functional #include memory #include Common.hpp #include Protocol.hpp #include PInetAddr.hpp #include Exitcode.hpp using namespace LogModule; #define defaultlistenfd -1 #define backlog 15 using namespace SockModule; using ioserverice_t std::functionvoid(std::shared_ptrSocket sock, InetAddr addr); class TcpServer { public: TcpServer(ioserverice_t func, uint16_t port) : _listenfd(defaultlistenfd), _isrunning(false), _func(func), _listensock(std::make_uniqueTcpSocket()) { _listensock-BuildTCPMethod(port); } void Run() { _isrunning true; while (_isrunning) { InetAddr client; auto addr _listensock-Accept(client); pid_t id fork(); if (id 0) { LOG(LEVEL::FATAL) fork err\n; exit(FORK_ERR); } else if (id 0) { LOG(LEVEL::DEBUG) accept one; close(_listenfd); if (fork() 0) exit(OK); _func(addr, client); exit(OK); } else { addr-Close(); waitpid(id, nullptr, WNOHANG); } } } ~TcpServer() { } private: int _listenfd; bool _isrunning; std::unique_ptrSocket _listensock; ioserverice_t _func; };Protocol.hpp 协议定制该协议层是网络计算器的核心中间层承接 “网络通信层” 与 “业务计算层”核心目标是解决 TCP 字节流无边界问题、实现请求 / 响应的跨平台序列化与反序列化最终完成 “客户端请求解析→调用计算逻辑→响应结果封装回传” 的全流程整体思路可拆解为以下四部分一、核心数据结构设计Request/Response 封装 JSON 序列化1. Request请求类作用封装客户端发送的计算请求操作数 x、操作数 y、运算符 oper核心能力序列化Serialization将 x/y/oper 封装为 JSON 字符串纯文本格式规避大小端 / 平台差异反序列化Deserialization将接收到的 JSON 字符串解析为 x/y/oper供计算层调用提供 GetX ()/GetY ()/GetOper () 方法暴露计算所需的核心数据。2. Response响应类作用封装计算结果result与状态码code标识计算是否成功核心能力序列化Serialization将 result/code 转为 JSON 字符串便于网络传输反序列化Deserialization将服务端返回的 JSON 字符串解析为结果和状态码提供 SetResult ()/SetCode ()/ShowResult () 方法设置 / 展示响应数据。二、应用层报文封装解决 TCP 字节流无边界问题TCP 是面向字节流的协议存在粘包 / 拆包问题因此自定义应用层报头格式核心思路是 “长度 分隔符 数据 分隔符”1. 编码Encode封装完整报文输入序列化后的 JSON 字符串如 Request/Response 的 JSON 数据逻辑计算 JSON 字符串的长度转为字符串按长度\r\nJSON数据\r\n拼接成完整报文目的通过 “长度前缀” 明确消息边界避免粘包 / 拆包导致的解析错误。2. 解码Decode解析完整报文输入从套接字读取的字节流缓冲区、用于存储解析后 JSON 数据的指针逻辑查找第一个分隔符\r\n提取长度前缀并转为整数校验缓冲区数据长度是否满足 “长度前缀 分隔符 JSON 数据 分隔符” 的总长度若满足截取对应长度的 JSON 数据删除缓冲区中已解析的部分若不满足返回 false等待后续数据目的从字节流中精准提取完整的 JSON 消息保证解析的准确性。三、服务端核心逻辑Getrequest 处理客户端请求作为服务端核心回调方法绑定到 TCP 服务器的客户端连接事件完整流程循环接收数据从客户端套接字读取数据追加到缓冲区队列解码提取完整报文调用 Decode () 从缓冲区解析出完整的 JSON 请求字符串反序列化获取请求参数将 JSON 字符串反序列化为 Request 对象提取 x/y/oper调用计算逻辑通过注入的回调函数计算层 Cal::Execute处理请求得到 Response 响应序列化 编码回传将 Response 序列化为 JSON 字符串通过 Encode () 封装成完整报文发送给客户端异常处理若客户端断开连接n0或接收错误n0记录日志并退出循环。四、客户端辅助逻辑BuildRequestString/Getresponse1. BuildRequestString构建客户端请求报文输入x/y/oper逻辑创建 Request 对象→序列化为 JSON→调用 Encode () 封装成完整报文直接供客户端发送。2. Getresponse解析服务端响应输入客户端套接字、响应缓冲区、Response 指针逻辑循环接收服务端数据追加到缓冲区调用 Decode () 解析出完整的 JSON 响应字符串将 JSON 字符串反序列化为 Response 对象返回解析结果异常处理服务端断开 / 接收错误时记录日志并返回 false。#pragma once #include Common.hpp #include Socket.hpp #include Log.hpp #include PInetAddr.hpp using namespace SockModule; using namespace LogModule; class Request { public: Request() { } Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper) { } std::string Serialization() { Json::Value root; root[X] _x; root[Y] _y; root[OPER] _oper; Json::FastWriter writer; std::string s writer.write(root); return s; } bool Deserialization(std::string in) { Json::Value root; Json::Reader reader; bool ok reader.parse(in, root); if (ok) { _x root[X].asInt(); _y root[Y].asInt(); _oper root[OPER].asInt(); } return ok; } int GetX() { return _x; } int GetY() { return _y; } char GetOper() { return _oper; } private: int _x; int _y; char _oper; }; class Response { public: Response() { } Response(int result, int code) : _code(code), _result(result) { } std::string Serialization() { Json::Value root; root[CODE] _code; root[RESULT] _result; Json::FastWriter writer; std::string s writer.write(root); return s; } bool Deserialization(std::string in) { Json::Value root; Json::Reader reader; bool ok reader.parse(in, root); if (ok) { _code root[CODE].asInt(); _result root[RESULT].asInt(); } return ok; } void SetResult(int result) { _result result; } void SetCode(int code) { _code code; } void ShowResult() { std::cout result is : _result [ _code ] std::endl; } private: int _code; int _result; }; const std::string sep \r\n; using func_t std::functionResponse(Request req); class Protocol { public: Protocol() { } Protocol(func_t func) : _func(func) { } // 应用层报头包装 length sep message sep std::string Encode(const std::string msg) { std::string len std::to_string(msg.size()); return len sep msg sep; } bool Decode(std::string msg, std::string *package) { int pos msg.find(sep); if (pos std::string::npos) { return false; } std::string package_len_str msg.substr(0, pos); int package_len_int std::stoi(package_len_str); int target_len package_len_int sep.size() * 2 package_len_str.size(); if (msg.size() target_len) { std::cout std::endl; return false; } *package msg.substr(pos sep.size(), package_len_int); msg.erase(0, target_len); return true; } void Getrequest(std::shared_ptrSocket sock, InetAddr client) { std::string buffer_queue; while (true) { int n sock-Recv(buffer_queue); if (n 0) { std::string Json_str; std::cout recv success: buffer_queue; bool ok Decode(buffer_queue, Json_str); if (!ok) { std::cout decode fail std::endl; continue; } // 拿到完整报文 Request req; bool des req.Deserialization(Json_str); if (!des) { continue; } Response res _func(req); std::string result res.Serialization(); std::string send_str Encode(result); sock-Send(send_str); } else if (n 0) { LOG(LEVEL::INFO) client: client.StringIp() Quit!; break; } else { LOG(LEVEL::WARNING) client: client.StringIp() , recv error; break; } } } std::string BuildRequestString(int x, int y, char oper) { Request req(x, y, oper); std::string json_req req.Serialization(); return Encode(json_req); } bool Getresponse(std::shared_ptrSocket sock, std::string resp_buff, Response *res) { while (true) { int n sock-Recv(resp_buff); if (n 0) { std::string Json_str; while (Decode(resp_buff, Json_str)) { res-Deserialization(Json_str); } return true; } else if (n 0) { LOG(LEVEL::WARNING) server quit!\n; return false; } else { LOG(LEVEL::FATAL) recv error\n; return false; } return false; } return true; } ~Protocol() {} private: func_t _func; };NetCal.hpp 业务逻辑实现#pragma once #include Protocol.hpp #include iostream class Cal { public: Response Execute(Request req) { Response res(0, 0); switch (req.GetOper()) { case : res.SetResult(req.GetX() req.GetY()); std::cout req.GetX() req.GetY(); break; case -: res.SetResult(req.GetX() - req.GetY()); break; case *: res.SetResult(req.GetX() * req.GetY()); break; case /: if (req.GetY() 0) { res.SetCode(1); // 防止除0导致程序崩溃直接返回错误码1 } else { res.SetResult(req.GetX() / req.GetY()); } break; case %: if (req.GetY() 0) { res.SetCode(2); // 防止模0导致程序崩溃直接返回错误码2 } else { res.SetResult(req.GetX() % req.GetY()); } break; default: break; } return res; } };其他文件大部分是对变量进行面向对象的封装Log.hpp#pragma once #include string #include iostream #include Mutex.hpp #include cstdio #include sstream #include fstream #include unistd.h #include sys/types.h #include memory #include filesystem using namespace MutexModule; namespace LogModule { #define gsep \r\n class LogStrategy { public: ~LogStrategy() default; virtual void SyncLog(const std::string info) 0; }; // 显示器打印 class ScreenStratey : public LogStrategy { public: void SyncLog(const std::string info) override { LockGuard _lock(_mutex); std::cout info gsep; } private: Mutex _mutex; }; // 指定文件打印 std::string defaultpath ./Log; std::string defaultfile Mylog.log; class FileStrategy : public LogStrategy { public: FileStrategy(const std::string path defaultpath, const std::string filename defaultfile) : _path(path), _filename(filename) { if (std::filesystem::exists(_path)) { return; } try { std::filesystem::create_directories(_path); } catch (const std::filesystem::filesystem_error e) { std::cerr e.what() \n; } } void SyncLog(const std::string message) override { LockGuard lockguard(_mutex); std::string filename _path (_path.back() / ? : /) _filename; // ./log/ my.log std::ofstream out(filename, std::ios::app); // 追加写入的 方式打开 if (!out.is_open()) { return; } out message gsep; out.close(); } ~FileStrategy() {}; private: std::string _path; std::string _filename; Mutex _mutex; }; enum class LEVEL { DEBUG, INFO, WARNING, ERROR, FATAL }; std::string leveltos(const LEVEL level) { switch (level) { case LEVEL::DEBUG: return DEBUG; case LEVEL::INFO: return INFO; case LEVEL::WARNING: return WARNING; case LEVEL::ERROR: return ERROR; case LEVEL::FATAL: return FATAL; default: return UNKNOW; } } std::string GetTime() { time_t curr time(nullptr); struct tm curr_tm; localtime_r(curr, curr_tm); char timebuffer[128]; snprintf(timebuffer, sizeof(timebuffer), %4d-%02d-%02d %02d:%02d:%02d, curr_tm.tm_year 1900, curr_tm.tm_mon 1, curr_tm.tm_mday, curr_tm.tm_hour, curr_tm.tm_min, curr_tm.tm_sec); return timebuffer; } // 默认执行类 class Logger { public: Logger() { Enable_Screen_log_Strategy(); } void Enable_Screen_log_Strategy() { ffulsh_strategy std::make_uniqueScreenStratey(); } void Enable_File_log_Strategy() { ffulsh_strategy std::make_uniqueFileStrategy(); } class Logmessage { public: Logmessage(const LEVEL level, std::string filename, int line, Logger logger) : _ctime(GetTime()), _level(level), _pid(getpid()), _filename(filename), _line(line), _logger(logger) { std::stringstream ss; ss [ _ctime ] [ leveltos(_level) ] [ _pid ] [ _filename ] [ _line ] - ; finfo ss.str(); } template typename T Logmessage operator(const T info) { std::stringstream ss; ss info; finfo ss.str(); return *this; } ~Logmessage() { _logger.ffulsh_strategy-SyncLog(finfo); } private: std::string _ctime; LEVEL _level; pid_t _pid; std::string _filename; int _line; Logger _logger; std::string finfo; // 完整信息 }; Logmessage operator()(LEVEL level, std::string name, int line) { return Logmessage(level, name, line, *this); } ~Logger() { } private: std::unique_ptrLogStrategy ffulsh_strategy; }; Logger logger; #define LOG(level) logger(level, __FILE__, __LINE__) #define Enable_Screen_log_Strategy logger.Enable_Screen_log_Strategy() #define Enable_File_log_Strategy logger.Enable_File_log_Strategy() } // 日志格式[ctime][level][pid][filename][line]Mutex.hpp// Mutex.hpp #pragma once #include iostream #include pthread.h namespace MutexModule { class Mutex { public: Mutex() { pthread_mutex_init(_mutex, nullptr); } pthread_mutex_t *Get() { return _mutex; } void Lock() { int n pthread_mutex_lock(_mutex); (void)n; } void Unlock() { int n pthread_mutex_unlock(_mutex); (void)n; } ~Mutex() { pthread_mutex_destroy(_mutex); } private: pthread_mutex_t _mutex; }; Mutex defaultmutex; class LockGuard { public: LockGuard(Mutex mutex defaultmutex) : _mutex(mutex) { _mutex.Lock(); } ~LockGuard() { _mutex.Unlock(); } private: Mutex _mutex; }; }ExitCode.hpp#pragma once enum Exitcode{ OK0, SOCK_ERR, BIND_ERR, LISTEN_ERR, ACCEPT_ERR, CONNECT_ERR, USAGE_ERR, FORK_ERR, };Common.hpp#pragma once #include iostream #include sys/types.h #include sys/socket.h #include string.h #include arpa/inet.h #include jsoncpp/json/json.h #include netinet/in.h #include functional #include cstdio #include sys/wait.h #include string #include unistd.h #define CONV(addr) ((sockaddr *)addr)PInetAddr.hpp#pragma once #include Common.hpp class InetAddr { public: InetAddr(const sockaddr_in addr, std::string ip, uint16_t port) : _addr(addr), _ip(ip), _port(port) { } InetAddr() { } InetAddr(sockaddr_in addr) { SetAddr(addr); } InetAddr(std::string ip, uint16_t port) : _ip(ip), _port(port) { memset(_addr, 0, sizeof(_addr)); _addr.sin_port htons(port); inet_pton(AF_INET, ip.c_str(), _addr.sin_addr); _addr.sin_family AF_INET; } InetAddr(uint16_t port) : _ip(), _port(port) { memset(_addr,0,sizeof(_addr)); _addr.sin_port htons(port); _addr.sin_addr.s_addr INADDR_ANY; _addr.sin_family AF_INET; } void SetAddr(sockaddr_in addr) { _addr addr; _port ntohs(addr.sin_port); char ipbuffer[64]; inet_ntop(AF_INET, addr.sin_addr, ipbuffer, sizeof(addr)); _ip ipbuffer; } std::string StringIp() { return _ip; } uint16_t GetPort() { return _port; } sockaddr_in Getaddr() { return _addr; } const struct sockaddr *NetAddrPtr() { return CONV(_addr); } socklen_t NetAddrLen(){ return sizeof(_addr); } private: std::string _ip; uint16_t _port; sockaddr_in _addr; };Socket.hpp#pragma once #include Common.hpp #include PInetAddr.hpp #include Log.hpp #include Exitcode.hpp const int defaultbacklog 16; using namespace LogModule; namespace SockModule { class Socket { public: virtual void CreateSocket() 0; virtual void Bind(uint16_t port) 0; virtual void Listen(int backlog) 0; virtual void Close() 0; virtual std::shared_ptrSocket Accept(InetAddr *addr) 0; virtual int Send(std::string buffer) 0; virtual int Recv(std::string *buffer) 0; virtual int Connect(std::string server_ip, uint16_t port) 0; ~Socket() { } public: void BuildTCPMethod(uint16_t port, int backlog defaultbacklog) { CreateSocket(); Bind(port); Listen(backlog); LOG(LEVEL::DEBUG) bulid success\n; } void BuildTcpClientMethod() { CreateSocket(); } }; class TcpSocket : public Socket { public: TcpSocket() { } TcpSocket(int fd) : _sockfd(fd) { } void CreateSocket() override { _sockfd ::socket(AF_INET, SOCK_STREAM, 0); if (_sockfd 0) { LOG(LEVEL::FATAL) socket error\n; exit(SOCK_ERR); } LOG(LEVEL::DEBUG) socket success\n; } void Bind(uint16_t port) override { InetAddr local(port); int n bind(_sockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LEVEL::FATAL) bind error\n; exit(BIND_ERR); } LOG(LEVEL::DEBUG) bind success\n; } void Listen(int backlog defaultbacklog) override { int n ::listen(_sockfd, backlog); if (n 0) { LOG(LEVEL::FATAL) listen error\n; exit(LISTEN_ERR); } LOG(LEVEL::DEBUG) listen success\n; } std::shared_ptrSocket Accept(InetAddr *addr) override { sockaddr_in peer; socklen_t len sizeof(peer); int fd accept(_sockfd, CONV(peer), len); addr-SetAddr(peer); if (fd 0) { LOG(LEVEL::WARNING) accept error\n; } return std::make_sharedTcpSocket(fd); } int Send(std::string msg) override { return send(_sockfd, msg.c_str(), msg.size(), 0); } int Recv(std::string *out) override { char buffer[1024]; int n recv(_sockfd, buffer, sizeof(buffer) - 1, 0); if (n 0) { buffer[n] 0; *out buffer; } return n; } int Connect(std::string server_ip, uint16_t port) override { InetAddr server(server_ip, port); return ::connect(_sockfd, CONV(server.Getaddr()), sizeof(sockaddr)); } void Close() override { close(_sockfd); } private: int _sockfd; }; };

相关新闻