Linux网络 - json,网络计算服务器与客户端改进

文章目录

  • 前言
  • 一、json
    • 1.引入库
    • 2. 使用步骤
    • 2.Calculator.hpp
    • 3.Task.hpp
    • 4.serverCal.hpp
  • 新客户端


前言

本章内容主要对上一章的网络计算器客户端和服务器进行一些Bug修正与功能改进。 并学习如何使用json库和daemon函数。


一、json

在我们自己的电脑上一些软件的文件夹中,我们经常能看到.json后缀的文件,那么这种文件是用来干什么的呢?

这就要说到我们上节课所讲的序列化和反序列化了,相信如果大家自己如果尝试写了一遍之后,会发现序列化和反序列化还是比较难写的。

而市面上,是存在这么一个库被广泛引用来做序列化和反序列化,他就是json库。

json库是一个第三方库,所以我们的Linux服务器一般是不自带的,需要下载安装。

sudo yum install -y jsoncpp-devel

安装后,它的头文件位于

ls /usr/include/jsoncpp/json

在这里插入图片描述

因为是第三方库,所以它也需要链接动态库。

ls /lib64/libjsoncpp.so

在这里插入图片描述

1.引入库

代码如下(示例):

#include <jsoncpp/json/json.h>

g++编译

Lib=-ljsoncpp
serverCal:main.ccg++ -o $@ $^ -std=c++11 -lpthread $(Lib)clientCal:clientCal.ccg++ -o $@ $^ -std=c++11 $(Lib)

2. 使用步骤

#pragma once
#include <iostream>
#include <string>
#include "log.hpp"
#include <jsoncpp/json/json.h>extern Log lg;
const char blank_space_sep = ' ';
const char protocol_sep = '\n';enum Code
{Div_Zero_Err = 1,Mod_Zeor_Err,Operatorr_Err,Float_Mod_Err
};enum Type
{Type_Int = 1,Type_Double = 2
};// 多态版本bool CheckType(std::string &in_str, int* type)
{Json::Value root;Json::Reader reader;bool suc = reader.parse(in_str, root);if (!suc){lg(Warning, "Deserialize Failed...");return false;}*type = root["type"].asInt();return true;
}class Request
{
public:Request() {}Request(char op): _op(op) {}virtual bool serialize() {}virtual bool deserialize() {}public:char _op;
};class IntRequest : public Request
{
public:IntRequest() {}IntRequest(int x, int y, char op): _x(x), _y(y), Request(op) {}virtual bool serialize(std::string *out_str){Json::Value root;root["x"] = _x;root["y"] = _y;root["op"] = _op;root["type"] = 1;Json::FastWriter writer;*out_str = writer.write(root);return true;}virtual bool deserialize(std::string &in_str){Json::Value root;Json::Reader reader;bool suc = reader.parse(in_str, root);if (!suc){lg(Warning, "Request Deserialize Failed...");return false;}_x = root["x"].asInt();_y = root["y"].asInt();_op = root["op"].asInt();Json::FastWriter writer;in_str.erase(0, writer.write(root).size());std::cout << "已经删除已解析报头..." << std::endl;return true;}public:int _x;int _y;
};class DoubleRequest : public Request
{
public:DoubleRequest() {}DoubleRequest(double x, double y, char op): _x(x), _y(y), Request(op) {}virtual bool serialize(std::string *out_str){Json::Value root;root["x"] = _x;root["y"] = _y;root["op"] = _op;root["type"] = 2;Json::FastWriter writer;*out_str = writer.write(root);return true;}virtual bool deserialize(std::string &in_str){Json::Value root;Json::Reader reader;bool suc = reader.parse(in_str, root);if (!suc){lg(Warning, "Request Deserialize Failed...");return false;}_x = root["x"].asDouble();_y = root["y"].asDouble();_op = root["op"].asInt();Json::FastWriter writer;in_str.erase(0, writer.write(root).size());return true;}public:double _x;double _y;
};class Respond
{
public:Respond() {}Respond(int code): _code(code) {}virtual bool serialize() {}virtual bool deserialize() {}public:int _code = -1;
};class IntRespond : public Respond
{
public:IntRespond() {}IntRespond(int result, int code): _result(result), Respond(code) {}virtual bool serialize(std::string *out_str){Json::Value root;root["result"] = _result;root["code"] = _code;root["type"] = 1;Json::FastWriter writer;*out_str = writer.write(root);return true;}virtual bool deserialize(const std::string &in_str){Json::Value root;Json::Reader reader;bool suc = reader.parse(in_str, root);if (!suc){lg(Warning, "Respond Deserialize Failed...");return false;}_result = root["result"].asInt();_code = root["code"].asInt();return true;}public:int _result;
};class DoubleRespond : public Respond
{
public:DoubleRespond() {}DoubleRespond(double result, int code): _result(result), Respond(code) {}virtual bool serialize(std::string *out_str){Json::Value root;root["result"] = _result;root["code"] = _code;root["type"] = 2;Json::FastWriter writer;*out_str = writer.write(root);return true;}virtual bool deserialize(const std::string &in_str){Json::Value root;Json::Reader reader;bool suc = reader.parse(in_str, root);if (!suc){lg(Warning, "Respond Deserialize Failed...");return false;}_result = root["result"].asDouble();_code = root["code"].asInt();return true;}public:double _result;
};

2.Calculator.hpp

#pragma once
#include "protocol.hpp"class Calculator
{
public:Calculator() {}IntRespond calculate(const IntRequest &rq){IntRespond rs;switch (rq._op){case '+':rs._result = rq._x + rq._y;break;case '-':rs._result = rq._x - rq._y;break;case '*':rs._result = rq._x * rq._y;break;case '/':if (rq._y == 0){lg(Warning, "Found Div Zero Error...");rs._code = Div_Zero_Err;return rs;}rs._result = rq._x / rq._y;break;case '%':if (rq._y == 0){lg(Warning, "Found Mod Zero Error...");rs._code = Mod_Zeor_Err;return rs;}rs._result = rq._x % rq._y;break;default:lg(Warning, "Found Operator Error...");rs._code = Operatorr_Err;return rs;}rs._code = 0;return rs;}DoubleRespond calculate(const DoubleRequest &rq){DoubleRespond rs;switch (rq._op){case '+':rs._result = rq._x + rq._y;break;case '-':rs._result = rq._x - rq._y;break;case '*':rs._result = rq._x * rq._y;break;case '/':if (rq._y == 0){lg(Warning, "Found Div Zero Error...");rs._code = Div_Zero_Err;return rs;}rs._result = rq._x / rq._y;break;case '%':lg(Warning, "Float Mod Error...");rs._code = Float_Mod_Err;return rs;default:lg(Warning, "Found Operator Error...");rs._code = Operatorr_Err;return rs;}rs._code = 0;return rs;}
};

3.Task.hpp

#pragma once
#include "Socket.hpp"
#include "protocol.hpp"
#include "Calculator.hpp"
class Task
{
public:Task(int socket_fd): _socket_fd(socket_fd){}void IntHandle(std::string &message){IntRequest rq;Calculator cal;// 因为可能message里面已经存在了多个报文,所以就需要一次性多次处理if (!rq.deserialize(message)){// 反序列化失败说明里面的数据可能出现数据丢失等情况,出现这种情况说明我们的报文数据不再可信,最直接的办法就是丢弃全部报文!message = "";return;}IntRespond rs = cal.calculate(rq);std::string res;rs.serialize(&res);printf("%d %c %d = %d\n", rq._x, rq._op, rq._y, rs._result);write(_socket_fd, res.c_str(), res.size());}void DoubleHandle(std::string &message){DoubleRequest rq;Calculator cal;// 因为可能message里面已经存在了多个报文,所以就需要一次性多次处理if (!rq.deserialize(message)){// 反序列化失败说明里面的数据可能出现数据丢失等情况,出现这种情况说明我们的报文数据不再可信,最直接的办法就是丢弃全部报文!message = "";return;}DoubleRespond rs = cal.calculate(rq);std::string res;rs.serialize(&res);printf("%lf %c %lf = %lf\n", rq._x, rq._op, rq._y, rs._result);write(_socket_fd, res.c_str(), res.size());}void run(){char in_buffer[1024];std::string message = "";while (true){memset(in_buffer, 0, sizeof in_buffer);int n = read(_socket_fd, (void *)in_buffer, sizeof in_buffer - 1);if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...", _socket_fd);break;}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", _socket_fd);break;}in_buffer[n] = 0;message = in_buffer;std::cout << "报文大小: " << message.size() << " ,报文内容: " << message << std::endl;// 判断发来的数据类型while (!message.empty()){int type;if (!CheckType(message, &type)){//报文内容出现问题message = "";break;}if (type == 1){IntHandle(message);}else if (type == 2){DoubleHandle(message);}else{lg(Warning, "Type Error, type: %d ...", type);}}}}void operator()(){run();close(_socket_fd);}~Task(){}private:int _socket_fd;
};

4.serverCal.hpp

#pragma once#include "Socket.hpp"
#include "protocol.hpp"
#include "threadPool.hpp"
#include "Task.hpp"
class ServerCal
{
public:ServerCal(){}void Init(const int sinfamily, const std::string &ip, const uint16_t port){_listensock.Init();_listensock.Bind(sinfamily, ip, port);_listensock.Listen();}void Run(){daemon(0, 0);  //仅此这里添加了一个守护线程功能ThreadPool<Task> *tp = ThreadPool<Task>::GetInstance();tp->Start();struct sockaddr_in client;while (true){memset(&client, 0, sizeof client);socklen_t len;int socketfd = _listensock.Accept(&client, &len);if (socketfd < 0)continue;tp->Push(socketfd);}}private:Socket _listensock;
};

这里我添加了守护线程的功能,使用的是系统库自带的函数。
在这里插入图片描述
nochdir如果被设为0,则更改工作路径为“/”根目录,否则则什么也不敢。
noclose如果被设为0,则将标准输入输出错误重定向到/dev/null文件中,/dev/null文件我们上章是讲过的。


其他的文件我没有变动,需要的可以在我上一个文章复制粘贴或者到我的gitee自行拷贝。

新客户端

#include "Socket.hpp"
#include "protocol.hpp"#define VALID_OP 5
const char valid_operator[VALID_OP] = {'+', '-', '*', '/', '%'};static int localfd = -1;void Usage(const char *mes)
{std::cout << "Usage: " << mes << " ip[xxx.xxx.xxx.xxx] port[8080-9000]" << std::endl;
}bool __CheckNumber(const std::string &str)
{for (const char c : str){if ((!isdigit(c)) && (c != '.')){return false;}}return true;
}bool __CheckOp(const std::string &op)
{if (op.size() != 1){return false;}for (int i = 0; i < VALID_OP; i++){if (op.find(valid_operator[i]) != std::string::npos)break;if (i == 4){return false;}}return true;
}bool CheckSafe(const std::string &x, const std::string &op, const std::string &y, int *type)
{if (!__CheckOp(op)){std::cout << "Helper: 使用了除 + - * / % 以外的运算符" << std::endl;return false;}if (!__CheckNumber(x) || !__CheckNumber(y)){std::cout << "Helper: 请输入正确的数字" << std::endl;return false;}if ((x.find('.') != std::string::npos) || (y.find('.') != std::string::npos)){// 说明这是浮点数运算*type = 2;return true;}*type = 1;return true;
}void IntHandle(const std::string &x, const std::string &op, const std::string &y, std::string &message)
{IntRequest rq;IntRespond rs;rq._x = std::stoi(x);rq._y = std::stoi(y);rq._op = op[0];rq.serialize(&message);write(localfd, message.c_str(), message.size());// 开始等待结果char buffer[1024];int n = read(localfd, buffer, sizeof buffer - 1);if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...", localfd);exit(1);}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", localfd);exit(2);}buffer[n] = 0;std::string res = buffer;std::cout << res << std::endl;rs.deserialize(res);if (rs._code != 0){switch (rs._code){case 1:std::cout << "出现除0错误" << std::endl;break;case 2:std::cout << "出现模0错误" << std::endl;break;case 3:std::cout << "使用了除 + - * / % 以外的运算符" << std::endl;break;default:std::cout << "发生未知错误" << std::endl;break;}return;}printf("%d %c %d = %d\n", rq._x, rq._op, rq._y, rs._result);
}void DoubleHandle(const std::string &x, const std::string &op, const std::string &y, std::string &message)
{DoubleRequest rq;DoubleRespond rs;rq._x = std::stod(x);rq._y = std::stod(y);rq._op = op[0];rq.serialize(&message);write(localfd, message.c_str(), message.size());// 开始等待结果char buffer[1024];int n = read(localfd, buffer, sizeof buffer - 1);if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...", localfd);exit(1);}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", localfd);exit(2);}buffer[n] = 0;std::string res = buffer;std::cout << res << std::endl;rs.deserialize(res);if (rs._code != 0){switch (rs._code){case 1:std::cout << "出现除0错误" << std::endl;break;case 2:std::cout << "出现模0错误" << std::endl;break;case 3:std::cout << "使用了除 + - * / % 以外的运算符" << std::endl;break;default:std::cout << "发生未知错误" << std::endl;break;}return;}printf("%lf %c %lf = %lf\n", rq._x, rq._op, rq._y, rs._result);
}int main(int argc, char *argv[])
{if (argc != 3){Usage("./clientCal");}Socket local;local.Init();int n = local.Connect(argv[1], argv[2]);if (n < 0){return 1;}localfd = local.Getfd();std::cout << "            简易计算器, 目前仅支持\" + - * / %\"运算符 " << std::endl;std::cout << "            数字和运算符请用空格或回车隔开" << std::endl;std::string x, op, y;int type;std::string message;while (true){std::cout << "请输入您的算式@ ";std::cin >> x >> op >> y;if (!CheckSafe(x, op, y, &type)){continue;}std::cout << type << std::endl;if (type == 1){IntHandle(x,op,y,message);}else if(type ==2){DoubleHandle(x,op,y,message);}else{lg(Warning, "Type Error, type: %d ...", type);exit(3);}}return 0;
}

添加了许多输入的安全检查,不检查引发的问题太多了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何删除电脑端口映射?

在使用电脑进行网络连接时&#xff0c;有时需要进行端口映射以实现不同设备之间的信息远程通信。当这些端口映射不再需要时&#xff0c;我们需要及时删除它们以确保网络的安全和稳定。本文将介绍如何删除电脑端口映射的方法。 操作系统自带的工具 大多数操作系统都提供了自带…

电机控制系列模块解析(29)—— 逆变器带输出LC滤波器

一般长线驱动&#xff08;港口和油矿&#xff09;和超高速电机&#xff08;高频&#xff09;等驱动系统可能会要求加装输出LC滤波器。 此图片来源于会议PPT 一、逆变器带输出LC滤波器 逆变器输出端配置LC滤波器&#xff08;电感L与电容C组成的无源滤波电路&#xff09;旨在改…

pdf文件如何防篡改内容

PDF文件防篡改内容的方法有多种&#xff0c;以下是一些常见且有效的方法&#xff0c;它们可以帮助确保PDF文件的完整性和真实性&#xff1a; 加密PDF文档&#xff1a; 原理&#xff1a;通过设置密码来保护PDF文档&#xff0c;防止未经授权的访问和修改。注意事项&#xff1a;密…

创新实训2024.06.03日志:完善Baseline Test框架、加入对Qwen-14B的测试

1. Baseline Test框架重构与完善 在之前的一篇博客中&#xff08;创新实训2024.05.29日志&#xff1a;评测数据集与baseline测试-CSDN博客&#xff09;&#xff0c;我介绍了我们对于大模型进行基线测试的一些基本想法和实现&#xff0c;包括一些基线测试的初步结果。 后来的一…

通过无障碍控制 Compose 界面滚动的实战和原理剖析

前言 针对 Compose UI 工具包&#xff0c;开发者不仅需要掌握如何使用新的 UI 组件达到 design 需求&#xff0c;更需要了解和实现与 UI 的交互逻辑。 比如 touch 事件、Accessibility 事件等等。 Compose 中对 touch 事件的处理和原理&#xff0c;笔者已经在《通过调用栈快…

【Linux】进程(9):进程控制1

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;9&#xff09;进程控制1&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1 fork函数2 进程终止&#xff08;A&#xff09;终止是…

java中异常-异常概述+异常体系结构

一、异常概述 1、什么是异常&#xff1f; java程序在运行时出现的不正常情况 2、java中提供的默认的异常处理机制 java中对java程序运行时可能会出现的每种不正常情况都创建了一个唯一对应的类&#xff0c;在java程序运行时如果出现不正常情况&#xff0c;java程序就会创建…

Java | Leetcode Java题解之第140题单词拆分II

题目&#xff1a; 题解&#xff1a; class Solution {public List<String> wordBreak(String s, List<String> wordDict) {Map<Integer, List<List<String>>> map new HashMap<Integer, List<List<String>>>();List<List…

USB Type-C 和 USB供电数据和电源角色

USB Type-C 连接器生态系统随着现代平台和设备需求的变化而不断发展。 USB Type-C 连接器生态系统可满足现代平台和设备不断变化的需求&#xff0c;并且符合更小、更薄且更轻便的外形设计趋势。此外&#xff0c;针对 Type-C 连接器修改 USB PD 有助于满足高耗电应用的需求。 …

数据总线、位扩展、字长

数据总线&#xff08;Data Bus&#xff09; 定义 数据总线是计算机系统中的一组并行信号线&#xff0c;用于在计算机内部传输数据。这些数据可以在中央处理器&#xff08;CPU&#xff09;、内存和输入/输出设备之间传输。 作用 数据传输&#xff1a;数据总线负责在计算机各…

c++【入门】求圆环的面积

限制 时间限制 : 1 秒 内存限制 : 128 MB 题目 如下图所示的圆环铁片&#xff0c;中间是空心的&#xff0c;已知圆环外圆的半径是r1厘米&#xff08;如&#xff1a;10cm&#xff09;&#xff0c;内圆半径是r2厘米&#xff08;如&#xff1a;6cm&#xff09;&#xff0c;请编…

心链13---主页切换功能 + loading特效 + 导航栏完善 + 队伍页接口修改

心链 — 伙伴匹配系统 直接取出所有用户&#xff0c;依次和当前用户计算分数&#xff0c;取 TOP N&#xff08;54 秒&#xff09; 优化方法&#xff1a; 切忌不要在数据量大的时候循环输出日志&#xff08;取消掉日志后 20 秒&#xff09;Map 存了所有的分数信息&#xff0c;占…

C++ | Leetcode C++题解之第140题单词拆分II

题目&#xff1a; 题解&#xff1a; class Solution { private:unordered_map<int, vector<string>> ans;unordered_set<string> wordSet;public:vector<string> wordBreak(string s, vector<string>& wordDict) {wordSet unordered_set(w…

SpringBoot+Vue网上购物商城系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 系统角色对应功能 用户商家管理员 系统功能截图

docker部署redis实践

1.拉取redis镜像 # 拉取镜像 sudo docker pull redis2.创建映射持久化目录 # 创建目录 sudo mkdir -p $PWD/redis/{conf,data}3. 运行redis 容器&#xff0c;查看当前redis 版本号 # 运行 sudo docker run --name redis -d -p 6379:6379 redis # 查看版本号 sudo docker ex…

java异常处理知识点总结

一.前提知识 首先当运行出错的时候&#xff0c;有两种情况&#xff0c;一种叫做“错误”&#xff0c;另一种叫做“异常”。错误指的是运行过程中遇到了硬件或操作系统出错&#xff0c;这种情况程序员是没办法处理的&#xff0c;因为这是硬件和系统的问题&#xff0c;不能靠代码…

使用GPT-soVITS再4060下2小时训练声音模型以及处理断句带来的声音模糊问题

B站UP主视频 感谢UP主“白菜工厂1145号员工”的“熟肉”&#xff0c;我这篇笔记就不展示整一个训练和推理流程&#xff0c;重点写的4060该注意的一些事项。如何解决断句模糊的问题&#xff0c;在本篇笔记的最末尾。 相关连接&#xff1a; 原项目github UP主的说明文档 1、训…

Linux的目录结构介绍和环境变量的设置

目录 前言一、系统环境二、Linux的目录结构2.1 Linux目录结构介绍2.2 Linux文件的路径描述2.2.1 绝对路径2.2.2 相对路径2.2.3 特殊的路径符 三、Linux的环境变量设置3.1 环境变量PATH3.2 关于$符的使用3.3 环境变量的设置 总结 前言 本篇文章介绍Linux的目录结构和环境变量的…

【云原生Kubernetes项目部署】k8s集群+高可用负载均衡层+防火墙

目录 环境准备 拓朴图 项目需求 一、Kubernetes 区域可采用 Kubeadm 方式进行安装 1.1所有节点master、node01、node02 1.2所有节点安装docker 1.3所有节点安装kubeadm&#xff0c;kubelet和kubectl 1.4部署K8S集群 1.4.1复制镜像和脚本到 node 节点&#xff0c;并在 …

html--酷炫背景引导主页

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>ZZVIPS酷炫背景引导主页</title><meta name"viewport" content"widthdevice-width,initial-scale1,maximum-scale1,user-scala…