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

embedded/2025/2/10 22:04:42/

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

        前一篇文章内容除了概念以外, 主要是利用序列化反序列化的知识点实现了一个网络计算器的服务端。 然后本节内容来实现以下客户端, 让服务端能够与客户端连接起来, 实现计算器的功能。 另外, 本节内容还会使用一下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 codeResponse 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 codeResponse 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 1const string blank_space_sep = " ";
const string protocol_sep = "\n";string Encode(string &content)  //content正文部分
{string package = to_string(content.size()); //先添加lenpackage += protocol_sep; //加反斜杠npackage += content;package += protocol_sep; //字符串末尾加上反斜杠nreturn 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 MySelfstring s = to_string(x);s += blank_space_sep;s += op;s += blank_space_sep;s += to_string(y);*out += s;return true;
#elseJson::Value root;root["x"] = x;root["y"] = y;root["op"] = op;Json::FastWriter w;*out = w.write(root);#endif}bool deserialize(string &in){
#ifdef MySelfsize_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;
#elseJson::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 MySelfstring s = to_string(result);s += blank_space_sep;s += to_string(code);*out += s;return true;
#elseJson::Value root;root["result"] = result;root["code"] = code;    #endif}bool deserialize(string &in){
#ifdef MySelfsize_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;
#elseJson::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具体是几, 表明对应的错误原因。};

运行结果

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

        再启动客户端:

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


http://www.ppmy.cn/embedded/161179.html

相关文章

C++11详解(四) -- 新的类功能和包装器

文章目录 1.新的类功能1.1 默认的移动构造和移动赋值1.2 成员变量声明的时候给缺省值1.3 default和delete1.4 final和override 2.STL中⼀些变化3.包装器3.1function3.2例题逆波兰表达式求值&#xff08;用了function&#xff09;题目解析代码 3.3 bind(绑定) 1.新的类功能 1.1…

(苍穹外卖)项目结构

苍穹外卖项目结构 后端工程基于 maven 进行项目构建&#xff0c;并且进行分模块开发。 1). 用 IDEA 打开初始工程&#xff0c;了解项目的整体结构&#xff1a; 对工程的每个模块作用说明&#xff1a; 序号名称说明1sky-take-outmaven父工程&#xff0c;统一管理依赖版本&…

《利用原始数据进行深度神经网络闭环 用于光学驻留空间物体检测》论文精读

Deep Neural Network Closed-loop with Raw Data for Optical Resident Space Object Detection 摘要 光学勘测是观测驻留空间物体和空间态势感知的重要手段。随着天文技术和还原方法的应用&#xff0c;宽视场望远镜在发现和识别驻留空间物体方面做出了重大贡献。然而&#x…

记录 | WPF基础学习自定义按钮

目录 前言一、解析Button.TemplateBorder和TemplateBinding 二、代码提供更新时间 前言 参考文章&#xff1a; 参考视频&#xff1a;【WPF入门教程 Visual Studio 2022】WPF界面开发入门 自己的感想 这里涉及到Template模板和事件。主要干两件事&#xff1a;1、template中的重写…

ubuntu server环境下使用mitmproxy代理

近日为了调试AWS连接的一个问题&#xff0c;需要查看HTTPS报文的内容&#xff0c;通过抓包的方式也可以。这里介绍通过mitmproxy的方式。因为是server环境&#xff0c;没有桌面&#xff0c;因此配置的过程是基于shell终端完成的。 安装mitmproxy sudo pip3 install mitmproxy…

周报1.0

补题补题(///&#xffe3;皿&#xffe3;)○&#xff5e; 牛客1(4):ABDG E:双生双宿之错: 小红定义一个数组是“双生数组”&#xff0c;当且仅当该数组大小为偶数&#xff0c;数组的元素种类恰好为 2种&#xff0c;且这两种元素的出现次数相同。例如{1,1,4,4,1,4} 是双生数组…

百度的冰桶算法

百度的冰桶算法&#xff08;Ice Bucket Algorithm&#xff09;是百度搜索引擎用于打击低质量内容的一种算法。该算法主要针对那些通过大量堆砌关键词、内容质量低下、用户体验差的网页进行惩罚&#xff0c;从而提升搜索结果的质量。 冰桶算法的核心目标&#xff1a; 打击低质…

使用PyCharm进行Django项目开发环境搭建

如果在PyCharm中创建Django项目 1. 打开PyCharm&#xff0c;选择新建项目 2.左侧选择Django&#xff0c;并设置项目名称 3.查看项目解释器初始配置 4.新建应用程序 执行以下操作之一&#xff1a; 转到工具| 运行manage.py任务或按CtrlAltR 在打开的manage.pystartapp控制台…