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

ops/2025/2/6 7:23:52/

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

相关文章

自制虚拟机(C/C++)(三、做成标准GUI Windows软件,扩展指令集,直接支持img软盘)

开源地址:VMwork 要使终端不弹出&#xff0c; #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") 还要实现jmp near 0x01类似的 本次的main.cpp #include <graphics.h> #include <conio.h> #include <windows.h> #includ…

Mac电脑上好用的压缩软件

在Mac电脑上&#xff0c;有许多优秀的压缩软件可供选择&#xff0c;这些软件不仅支持多种压缩格式&#xff0c;还提供了便捷的操作体验和强大的功能。以下是几款被广泛推荐的压缩软件&#xff1a; BetterZip 功能特点&#xff1a;BetterZip 是一款功能强大的压缩和解压缩工具&a…

2.4学习总结

洛谷1305代码 #include<stdio.h> #include<stdlib.h> struct treenode {char val;struct treenode* left;struct treenode* right; }; struct treenode* createnode(char val) {struct treenode* node (struct treenode*)malloc(sizeof(struct treenode));node-&…

Android 常用命令和工具解析之Battery Historian

Batterystats是包含在 Android 框架中的一种工具&#xff0c;用于收集设备上的电池数据。您可以使用adb bugreport命令抓取日志&#xff0c;将收集的电池数据转储到开发机器&#xff0c;并生成可使用 Battery Historian 分析的报告。Battery Historian 会将报告从 Batterystats…

WebSocket——环境搭建与多环境配置

一、前言&#xff1a;为什么要使用多环境配置&#xff1f; 在开发过程中&#xff0c;我们通常会遇到多个不同的环境&#xff0c;比如开发环境&#xff08;Dev&#xff09;、测试环境&#xff08;Test&#xff09;、生产环境&#xff08;Prod&#xff09;等。每个环境的配置和需…

探索 Copilot:开启智能助手新时代

探索 Copilot&#xff1a;开启智能助手新时代 在当今数字化飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着我们的工作和生活方式。而 Copilot 作为一款强大的 AI 助手&#xff0c;凭借其多样的功能和高效的应用&#xff0c;正在成为众…

HTML 字符实体

HTML 字符实体 在HTML中,字符实体是一种特殊的表示方式,用于在文档中插入那些无法直接通过键盘输入的字符。字符实体在网页设计和文档编写中扮演着重要的角色,尤其是在处理特殊字符、符号和数学公式时。以下是关于HTML字符实体的详细解析。 字符实体概述 HTML字符实体是一…

GGML、GGUF、GPTQ 都是啥?

GGML、GGUF和GPTQ是三种与大型语言模型(LLM)量化和优化相关的技术和格式。它们各自有不同的特点和应用场景,下面将详细解释: 1. GGML(GPT-Generated Model Language) 定义:GGML是一种专为机器学习设计的张量库,由Georgi Gerganov创建。它最初的目标是通过单一文件格式…