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

devtools/2024/12/29 12:47:39/

         前言:本节讲述序列化和反序列化的相关内容。 这节的内容是博主前一篇博客的续章, 里面用到了很多知识点都是前一篇文章的。 友友们如果要学习序列化反序列化, 直接看本篇文章是看不懂的, 请看前一篇文章: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/devtools/146030.html

相关文章

深度学习-论文即插即用模块1

[深度学习] 即插即用模块详解与实践 深度学习近年来已经成为人工智能的核心驱动力&#xff0c;各种模型和技术被广泛应用于图像处理、自然语言处理、语音识别等领域。然而&#xff0c;构建深度学习模型的过程通常复杂且耗时。为了提高开发效率并降低技术门槛&#xff0c;“即插…

模型 卡尼曼系统

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。直觉快思&#xff0c;理性慢想。 1 模型 卡尼曼系统的应用 1.1 直播购物APP中的卡尼曼系统应用案例 案例背景&#xff1a; 在直播购物APP中&#xff0c;平台通过展示单个用户的视角视频来向用户推荐…

计算机网络 (10)网络层

前言 计算机网络中的网络层&#xff08;Network Layer&#xff09;是OSI&#xff08;开放系统互连&#xff09;模型中的第三层&#xff0c;也是TCP/IP模型中的第二层&#xff0c;它位于数据链路层和传输层之间。网络层的主要任务是负责数据包从源主机到目的主机的路径选择和数据…

【PPTist】表格功能

前言&#xff1a;这篇文章来探讨一下表格功能是怎么实现的吧&#xff01; 一、插入表格 我们可以看到&#xff0c;鼠标移动到菜单项上出现的提示语是“插入表格” 那么就全局搜索一下&#xff0c;就发现这个菜单在 src/views/Editor/CanvasTool/index.vue 文件中 <Popov…

掌握软件工程基础:知识点全面解析【chap02】

chap02 软件项目管理 1.代码行度量与功能点度量的比较 1.规模度量 是一种直接度量方法。 代码行数 LOC或KLOC 生产率 P1L/E 其中 L 软件项目代码行数 E 软件项目工作量&#xff08;人月 PM&#xff09; P1 软件项目生产率&#xff08;LOC/PM&#xff09; 代码出错…

如何使用MySQL WorkBench操作MySQL数据库

1. 说明 最原始的对MySQL的数据库、表等信息的操作是在命令提示符中进行&#xff0c;但是这样的操作方式不是十分的方便&#xff0c;有些操作进行起来会比较麻烦&#xff0c;所以MySQL官方推出了一个对于MySQL数据库进行图形化管理的工具&#xff0c;那就是MySQL WorkBench&am…

期权懂|期权合约是如何划分月份的?如何换月移仓?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 期权合约是如何划分月份的&#xff1f;如何换月移仓&#xff1f; 合约月份&#xff1a;一般是指期权合约指定交易的月份&#xff0c;也可以理解成期权合约到期的月份&#xff0c…

【商城源码的开发环境】

商城源码的开发环境要求主要包括技术选型、硬件配置、软件配置以及安全性和性能优化。以下是一些商城源码开发环境的要求&#xff1a; 技术选型 编程语言&#xff1a;选择合适的编程语言&#xff0c;如PHP、Java、Python等&#xff0c;这取决于项目需求和团队的技术栈。 数据…