Linux网络 | 网络计算器客户端实现与Json的安装以及使用

Linux网络 | 网络计算器客户端实现与Json的安装以及使用
         前言:本节讲述序列化和反序列化的相关内容。 这节的内容是博主前一篇博客的续章, 里面用到了很多知识点都是前一篇文章的。 友友们如果要学习序列化反序列化, 直接看本篇文章是看不懂的, 请看前一篇文章:linux网络 | 序列化反序列化的概念 与 结合网络计算器深度理解-ZEEKLOG博客

        前一篇文章内容除了概念以外, 主要是利用序列化反序列化的知识点实现了一个网络计算器的服务端。 然后本节内容来实现以下客户端, 让服务端能够与客户端连接起来, 实现计算器的功能。 另外, 本节内容还会使用一下Json, 这是一个序列化反序列化解决方案,以后我们就不用自己手搓序列化反序列化了。 

        ps:本节内容友友们务必看完前上面的文章链接的文章哦! 

目录

ClientCal实现

命令行参数

connect

准备工作 

write 

read 

Json的使用

运行结果


ClientCal实现

命令行参数

        客户端同样要有命令行参数:

int main(int argc, char* argv[]) { if (argc != 3) { Usage(argv[0]); exit(0); } return 0; }

connect

        然后这个命令行参数第一个据说程序的名称, 第二个是表示要链接的服务器的IP地址, 第三个是表示要链接的服务器的端口号。  

        将ip地址和端口号获取之后就可以尝试进行连接, 链接的接口我们在socket套接字里面直接封装起来, 方便后面的使用:

 bool Connect(string serverip, uint16_t serverport) { sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(serverport); inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr)); connect(sockfd, (sockaddr*)&server, sizeof(server)); return true; }

        这个函数就是将要连接的服务器端的ip地址和端口号传送进去, 然后就向服务器进行连接。 

那么ClientCal里面的代码就是这样的:

#include<iostream> using namespace std; #include"tcpserver.hpp" #include<string> #include"socket.hpp" #include<time.h> #include<cstdlib> void Usage(string proc) { cout << "Usage: " << proc << endl; } int main(int argc, char* argv[]) { if (argc != 3) { Usage(argv[0]); exit(0); } // string serverip = argv[1]; uint16_t port = stoi(argv[2]); Socket sockfd; sockfd.SocketInit(); bool r = sockfd.Connect(serverip, port); if (!r) return 1; return 0; }

准备工作 

        接下来就是向服务器发送请求,我们这里发送请求使用rand进行随机选数字, 选两个数字和一个操作符, 然后打包成报文,写到网卡里面。  在发送请求之前先做一些准备工作:

 srand(time(nullptr) ^ getpid()); //种随机数种子 int cnt = 1; //对发送的请求个数进行计数, 我们只发送20次请求 string opers = "+-*/%"; //加减乘除运算符 string inbuffer_stream; //请求字节流缓冲区

write 

         然后我们就是先一下写入操作:

while (cnt <= 20) { cout << "第" << cnt++ << "次测试......" << endl; //计数这是第几次发送请求 //随机数,选一个随机数 int x = rand() % 100 + 1; usleep(1234); //选第二个随机数 int y = rand() % 100 ; usleep(4321); //选一个随机的运算符 char oper = opers[rand() % opers.size()]; //对两个随机数和运算符进行序列化 Request req(x, y, oper); req.DebugPrint(); //这个函数属于req里面的成员函数, 用于debug, 就是对请求进行一下打印, 方便观察。 代码后面展示 string package; req.Serialize(&package); //对序列化的请求打包成保温 package = Encode(package); cout << "这是最新的发出去的请求:\n" << package; //发送请求 write(sockfd.Fd(), package.c_str(), package.size()); }

         这是debug的函数, 这个函数是

    void DebugPrint()     {         cout << "构建请求完成: " << x << op << y <<  "?= " << endl;         }

read 

         客户端将数据发送过去之后, 服务端将数据进行处理,然后客户端就从对面接收数据,所以write函数后面就调用read函数:

 while (cnt <= 20) { cout << "第" << cnt++ << "次测试......" << endl; int x = rand() % 100 + 1; usleep(1234); int y = rand() % 100 ; usleep(4321); char oper = opers[rand() % opers.size()]; Request req(x, y, oper); req.DebugPrint(); string package; req.Serialize(&package); package = Encode(package); cout << "这是最新的发出去的请求:\n" << package; write(sockfd.Fd(), package.c_str(), package.size()); char buffer[128]; ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer)); //我们也无法保证我们能够读到一个完整的报文 if (n > 0) { buffer[n] = 0; inbuffer_stream += buffer; string content; bool r = Decode(inbuffer_stream, &content); //result code Response resp; r = resp.DeSerialize(content); resp.DebugPrint(); } sleep(1); }

         下面是全部代码:

#include<iostream> using namespace std; #include"tcpserver.hpp" #include<string> #include"socket.hpp" #include<time.h> #include<cstdlib> void Usage(string proc) { cout << "Usage: " << proc << endl; } int main(int argc, char* argv[]) { if (argc != 3) { Usage(argv[0]); exit(0); } // string serverip = argv[1]; uint16_t port = stoi(argv[2]); Socket sockfd; sockfd.SocketInit(); bool r = sockfd.Connect(serverip, port); if (!r) return 1; srand(time(nullptr) ^ getpid()); int cnt = 1; string opers = "+-*/%"; string inbuffer_stream; while (cnt <= 20) { cout << "第" << cnt++ << "次测试......" << endl; int x = rand() % 100 + 1; usleep(1234); int y = rand() % 100 ; usleep(4321); char oper = opers[rand() % opers.size()]; Request req(x, y, oper); req.DebugPrint(); string package; req.Serialize(&package); package = Encode(package); cout << "这是最新的发出去的请求:\n" << package; write(sockfd.Fd(), package.c_str(), package.size()); char buffer[128]; ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer)); //我们也无法保证我们能够读到一个完整的报文 if (n > 0) { buffer[n] = 0; inbuffer_stream += buffer; string content; bool r = Decode(inbuffer_stream, &content); //result code Response resp; r = resp.DeSerialize(content); resp.DebugPrint(); } sleep(1); } return 0; }

Json的使用

        Json在使用之前要先安装, centos下是yum install -y jsoncpp-devel,  Ubuntu下是:apt-get install libjsoncpp-dev。Ubuntu下建议安装前先update一下apt。

        如果安装后, 可以检查一下是否安装成功:(Ubuntu下是这样的路径)

        如果有上面这些文件, 说明就是安装成功了。 

        然后下面开始使用示例:

#include<iostream> #include<jsoncpp/json/json.h> #include<string> using namespace std; int main() { Json::Value root; root["x"] = 100; root["y"] = 200; root["op"] = "+"; root["dect"] = "this is a + oper"; // Json::FastWriter w; string res = w.write(root); //序列化,参数就是我们的root。 cout << res << endl; Json::Value v; Json::Reader r; //r就是一个方法, 利用r里面的方法将res字符串给V, 就完成了反序列化 r.parse(res, v); //这就是将反序列化的数据提取出来 int x = v["x"].asInt(); // int y = v["y"].asInt(); int op = v["op"].asInt(); string desc = v["desc"].asString(); return 0; }

        有了Json之后, 对于序列化和反序列化就可以改成Json版本的了, 这里使用条件编译进行控制, 保留代码痕迹。 注意, 序列化反序列化是serialize和deserialize, 而Encode和 Decode是对序列化的数据进行添加报头和解报头。Encode和Decode仍需要我们自己实现, 序列化和反序列化可以由Json代替: 

//协议 #pragma once #include<iostream> using namespace std; #include<string> #include<jsoncpp/json/json.h> // #define MySelf 1 const string; const string protocol_sep = "\n"; string Encode(string &content) //content正文部分 { string package = to_string(content.size()); //先添加len package += protocol_sep; //加反斜杠n package += content; package += protocol_sep; //字符串末尾加上反斜杠n return package; } bool Decode(string &package, string *content) { size_t pos = package.find(protocol_sep); if(pos == string::npos) return false; string len_str = package.substr(0, pos); size_t len = stoi(len_str); size_t total_len = len_str.size() + len + 2; if (package.size() <= total_len) return false; *content = package.substr(pos + 1, len); package.erase(0, total_len); return true; } //json, protobuf。可以帮助自动序列化发序列化, 也就是线程的序列化反序列化方案。 class Request { public: Request(){} Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper) { } bool serialize(string *out) { #ifdef MySelf string s = to_string(x); s += blank_space_sep; s += op; s += blank_space_sep; s += to_string(y); *out += s; return true; #else Json::Value root; root["x"] = x; root["y"] = y; root["op"] = op; Json::FastWriter w; *out = w.write(root); #endif } bool deserialize(string &in) { #ifdef MySelf size_t left = in.find(blank_space_sep); if (left == string::npos) return false; string part_x = in.substr(0, left); size_t right = in.rfind(blank_space_sep); if (right == string::npos) return false; string part_y = in.substr(right + 1); if (left + 2 != right) return false; op = in[left + 1]; x = stoi(part_x); y = stoi(part_y); return true; #else Json::Value root; Json::Reader r; r.parse(in, root); //反序列化 x = root["x"].asInt(); y = root["y"].asInt(); op = root["op"].asInt(); #endif } void DebugPrint() { cout << "构建请求完成: " << x << op << y << "?= " << endl; } public: int x; int y; char op; // + - * / % }; class Response { public: Response(){} Response(int res, int c) : result(res), code(c) { } bool serialize(string *out) { #ifdef MySelf string s = to_string(result); s += blank_space_sep; s += to_string(code); *out += s; return true; #else Json::Value root; root["result"] = result; root["code"] = code; #endif } bool deserialize(string &in) { #ifdef MySelf size_t pos = in.find(blank_space_sep); if (pos == string::npos) return false; string part_left = in.substr(0, pos); string part_right = in.substr(pos + 1, string::npos); result = stoi(part_left); code = stoi(part_right); return true; #else Json::Value root; Json::Reader r; r.parse(in, root); result = root["result"].asInt(); code = root["code"].asInt(); #endif } public: int result; int code; //0, 可信,否则!0具体是几, 表明对应的错误原因。 };

运行结果

        我们完成了客户端, 服务端上一节课也完成了。 现在我们就能让网络计算器跑起来了。 看一下运行结果:先启动服务端

        再启动客户端:

 ——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!! 

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk