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.rhkb.cn/news/348327.html

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

相关文章

顶顶通呼叫中心中间件-限制最大通话时间(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-限制最大通话时间(mod_cti基于FreeSWITCH) 一、最大通话时间 1、配置拨号方案 1、点击拨号方案 ->2、在框中输入通话最大时长->3、点击添加->4、根据图中配置->5、勾选continue。修改拨号方案需要等待一分钟即可生效 action"sched…

《Brave New Words 》2.2 阅读理解的未来,让文字生动起来!

Part II: Giving Voice to the Social Sciences 第二部分&#xff1a;为社会科学发声 The Future of Reading Comprehension, Where Literature Comes Alive! 阅读理解的未来&#xff0c;让文字生动起来&#xff01; Saanvi, a ninth grader in India who attends Khan World S…

Echarts 在折线图平滑位置处添加该处信息

文章目录 需求分析需求 分析 通过自定义折线图的标签(label)来实现。在 ECharts 中,可以通过设置 series 中的 label.normal.formatter 属性来实现这一点。 需要注意的是拐点处symbol不能设置为 none,否则会展示不出 label ,以下是一个示例代码,演示了如何在折线图的相邻…

超详解——Python 元组详解——小白篇

目录 1. 元组简介 创建元组 2. 元组常用操作 访问元组元素 切片操作 合并和重复 成员操作符 内置函数 解包元组 元组方法 3. 默认集合类型 作为字典的键 作为函数参数 作为函数的返回值 存储多种类型的元素 4.元组的优缺点 优点 缺点 5.元组的使用场景 数据…

如何保证数据库和缓存的一致性

背景&#xff1a;为了提高查询效率&#xff0c;一般会用redis作为缓存。客户端查询数据时&#xff0c;如果能直接命中缓存&#xff0c;就不用再去查数据库&#xff0c;从而减轻数据库的压力&#xff0c;而且redis是基于内存的数据库&#xff0c;读取速度比数据库要快很多。 更新…

《web应用技术》第十一次作业

1、验证过滤器进行权限验证的原理。 代码展示&#xff1a; Slf4j WebFilter(urlPatterns "/*") public class LoginCheckFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) thro…

计算机网络 —— 数据链路层(无线局域网)

计算机网络 —— 数据链路层&#xff08;无线局域网&#xff09; 什么是无线局域网IEEE 802.11主要标准及其特点&#xff1a; 802.11的MAC帧样式 我们来看看无线局域网&#xff1a; 什么是无线局域网 无线局域网&#xff08;Wireless Local Area Network&#xff0c;简称WLAN…

平板消解加热台-温度均匀,防腐蚀-实验室化学分析

DBF系列防腐电热板 是精致路合金加热板块表面经进口高纯实验级PFATeflon氟塑料防腐不粘处理&#xff0c;专为实验室设计的电加热产品&#xff0c;是样品前处理中&#xff0c;加热、消解、煮沸、蒸酸、赶酸等处理的得力助手。可以满足物理、化学、生物、环保、制药、食品、饮品…

【个人博客搭建】(23)购买服务器、域名、备案

1、服务器主要是为了有一个公网的IP地址&#xff0c;方便我们可以通过网络随时访问 2、域名是对IP地址的一个替代。简单说IP地址可能不方便记忆&#xff0c;但是自己配置的域名会简单些&#xff0c;另外暴露IP地址也不安全。(虽然也能通过域名找到IP) 3、备案。这是政策。简单所…

PBox iOS端的应用隐藏、图片视频加密软件

哈喽&#xff0c;大家下午好&#xff01;相信大家的手机中一定存在很多的私密内容&#xff0c;比如软件、照片、视频或者文档文件&#xff0c;很多都是不方便让外人看到的&#xff0c;此时就需要一款隐藏工具&#xff0c;市面上这类软件大部分都是收费的&#xff0c;应大家的需…

DETR实现目标检测(一)-训练自己的数据集

1、DETR架构 DETR&#xff08;Detection Transformer&#xff09;是一种新型的目标检测模型&#xff0c;由Facebook AI Research (FAIR) 在2020年提出。DETR的核心思想是将目标检测任务视为一个直接的集合预测问题&#xff0c;而不是传统的两步或多步预测问题。这种方法的创新…

升级和维护老旧LabVIEW程序

在升级老旧LabVIEW程序至64位环境时&#xff0c;需要解决兼容性、性能和稳定性等问题。本文从软件升级、硬件兼容性、程序优化、故障修复等多个角度详细分析。具体包括64位迁移注意事项、修复页面跳转崩溃、解决关闭程序后残留进程的问题&#xff0c;确保程序在新环境中的平稳运…

RainBond 制作应用并上架【以ElasticSearch为例】

文章目录 安装 ElasticSearch 集群第 1 步:添加组件第 2 步:查看组件第 3 步:访问组件制作 ElasticSearch 组件准备工作ElasticSearch 集群原理尝试 Helm 安装 ES 集群RainBond 制作 ES 思路源代码Dockerfiledocker-entrypoint.shelasticsearch.yml制作组件第 1 步:添加组件…

服务架构的设计原则

墨菲定律与康威定律 在系统设计的时候&#xff0c;可以依据于墨菲定律 任何事情都没有表面上看起来那么简单所有的事情都会比你预计的时间长可能出错的事总会出错担心的某一个事情的发送&#xff0c;那么它就更有可能发生 在系统划分的时候&#xff0c;可以依据康威定律 系…

python3的基本语法说明一

一. 简介 本文开始学习 python3 的基本语法。 二. python3的基本语法 1. 编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*- 上述…

录音转文字软件:一键让工作学习更高效

在职场这个大舞台上&#xff0c;每一场会议都是关键的演出&#xff0c;而会议记录就是这场演出的剧本。但剧本要整理得好&#xff0c;才能让演出更精彩&#xff0c;不是吗&#xff1f; 把那些长串的会议音频变成清晰的文字记录&#xff0c;听起来就像变魔术一样难。但不用担心…

MariaDB数据导入与导出操作演示

文章目录 整个数据库导出导入先删除库然后再导入 参考这里&#xff1a; MariaDB数据库导出导入. 整个数据库 该部分演示&#xff1a;导出数据库&#xff0c;然后重建数据库&#xff0c;并导入数据的整个过程。 导出 Win R &#xff0c;打开运行输入cmd并回车&#xff0c;然…

Unity与Js通信交互

目录 1.Js给Unity传递消息 2.Unity给Js传递消息 简介: Unity 与 JavaScript 通信交互是指在 Unity 项目中实现与 JavaScript 代码进行数据交换和功能调用的过程。 在 Unity 中&#xff0c;可以通过特定的接口和技术来与外部的 JavaScript 环境进行连接。这使得 Unity 能够利…

【git使用四】git分支理解与操作(详解)

目录 &#xff08;1&#xff09;理解git分支 主分支&#xff08;主线&#xff09; 功能分支 主线和分支关系 将分支合并到主分支 快速合并 非快速合并 git代码管理流程 &#xff08;2&#xff09;理解git提交对象 提交对象与commitID Git如何保存数据 示例讲解 &a…

探索乡村振兴新模式:发挥科技创新在乡村振兴中的引领作用,构建智慧农业体系,助力美丽乡村建设

随着科技的不断进步&#xff0c;乡村振兴工作正迎来前所未有的发展机遇。科技创新作为推动社会发展的重要力量&#xff0c;在乡村振兴中发挥着越来越重要的引领作用。本文旨在探讨如何发挥科技创新在乡村振兴中的引领作用&#xff0c;通过构建智慧农业体系&#xff0c;助力美丽…