【计网】从零开始掌握序列化 --- 实现网络计算器项目

在这里插入图片描述

​​​请各位保持头脑清醒,
​​​读些好书,做点有用的事,
​​​快快乐乐地生活。
​​​ --- 斯蒂芬·金 《肖申克的救赎》---

从零开始掌握序列化

  • 1 知识回顾
  • 2 服务器框架
  • 3 客户端框架
  • 4 运行测试

1 知识回顾

前面两篇文章学习中基础知识和三层结构的实现
我们学习了:

序列化与反序列化:

  1. 必要性:协议的本质是双方都认识的结构化数据,为了传输结构化的数据就需要进行序列化,为了从数据流中获取结构化数据就要进行反序列化!
  2. 本质:序列化的本质是将结构化的数据转换成字符串,将字符串发送给客户端。客户端根据协议进行反序列化获取到结构化数据!
  3. 序列化与反序列化的方法有很多种,可以自行编写也可以使用第三方库,比如JSON库

并且重新理解了TCP协议:

TCP协议

  1. 支持全双工通信:传输层会创建两个缓冲区:发送缓冲区和接收缓冲区。发送和接收是分开的,所以天然支持全双工
  2. 通信函数本质:read , write , send , recv本质上都是拷贝函数!他们都是讲数据拷贝到缓冲区中,并不关心缓冲区中的数据何时以何种方式发送给对方,系统负责缓冲区的刷新!
  3. 传输层是属于OS的,传输缓冲区的本质和文件缓冲区一样,在操作系统看起来都是向文件中进行刷新写入,用户不需要考虑!

最重要的是将Socket进行了程序重构,具体的细节在TCP协议中讲解过。这样将通信功能彻底解耦出来:

  1. 将socket系列操作分类封装,设计为基类,派生出Tcp和Udp两种具体的Socket!
  2. 基类都需要进行创建socket文件 、进行绑定、 进入listen 、获取链接、 申请链接…由于两种类的操作方式不一致,所以基类只需要进行一个声明就可以,具体实现在派生类中完成!
  3. 依照基础的方法进行组合就可以实现生成服务器端Socket和客户端Socket!

对应网络计算器的需求,我们设计了Request和Response两个结构体作为通信的协议!并且我们通过JSON库来进行协议内部的序列化与反序列化!为了保证可以获取完整的结构化数据,我们设计了独特的报文结构:
len\r\n{json}\r\n这样可以保证从数据流中获取完整的报文结构!!!

2 服务器框架

在这里插入图片描述
服务器的框架是基于这样的三层结构实现的:

  1. 传输层TcpServer:负责从Socket文件中获取链接,传输层不需要进行IO,获取到连接就让会话层通过连接获取数据!
  2. 会话层Service:根据传输层给的连接,从Sockfd文件中读取数据,解析出报文结构中的数据字符串,然后通过协议分离出结构化数据。该层只负责数据的解析,数据的处理交给应用层进行!
  3. 应用层Process:应用层是具有的业务逻辑,根据会话层解析出的数据,进行数据处理!这里使用的是网络计算器的业务逻辑,也就是执行加减乘除运算!

基于这样的结构我们上层的服务器代码逻辑是很好写的:

#include "TcpServer.hpp"
#include "Service.hpp"
#include "NetCal.hpp"
int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);//业务层NetCal cal;Service ser(std::bind(&NetCal::Calculator , &cal , std::placeholders::_1));//IO层std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(std::bind(&Service::IOExecute, &ser,std::placeholders::_1,std::placeholders::_2),port);//进入通信循环!tsvr->Loop();return 0;
}

可以看到我们只是使用了两次的bind绑定就实现了三层结构的实现,十分非简洁明了。只需等待客户端传入数据即可!

3 客户端框架

客户端的框架和服务端类似:

  1. 首先客户端在执行程序时需要传入服务器的IP地址和端口号!
  2. 然后通过封装的Socket类创建客户端Socket文件!对于IP地址和端口号的处理都封装在了类方法中,使用起来十分简单快捷!
#include <iostream>
#include "Socket.hpp"
#include "Protocol.hpp"using namespace socket_ns;int main(int argc, char *argv[])
{// 根据参数获取服务器IP 与 端口号if (argc != 3){std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;exit(0);}std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);// 工厂建立TcpSocket链接SockSPtr sock = std::make_shared<TcpSocket>();if (!sock->BuildClientSocket(ip, port)){std::cerr << "connect error!" << std::endl;exit(1);}std::string packagestream;//业务逻辑while (true){}return 0;
}

接下来我们来进行客户端数据通信的逻辑:

  1. 基础数据的获取这里为了快捷直接使用随机数进行初始化!
  2. 发送数据:根据协议快速构建Request,然后对其进行序列化,然后加入报头形成完整报文,发送给服务器
  3. 接收数据:从Socket文件中读取数据流,去除报头,检查是否具有完整报文,有完整报文就进行反序列化得到Response,打印结果即可!!!
int main(int argc, char *argv[])
{//...srand(time(nullptr));std::string arr = "+-*/%&^!";std::string packagestream;int cnt = 3;while (cnt--){// 传入数据int x = rand() % 50;usleep(1000);int y = rand() % 50;char oper = arr[y % arr.size()];// 1. 构建requestauto req = Factory::BuildRequestDefault();req->SetValue(x, y, oper);// 2. 进行序列化std::string jsonstr;req->Serialize(&jsonstr);std::cout << "jsonstr: " << jsonstr << std::endl;// 3. 添加报头std::string reqstr = Encode(jsonstr);// 4. 发送数据sock->Send(reqstr);// 5. 接收数据while (true){ssize_t n = sock->Recv(&packagestream);if (n <= 0)break;// 6. 去除报头std::string resstr = Decode(packagestream);std::cout << "resstr: " << resstr << std::endl;if (resstr.empty())continue;auto res = Factory::BuildResponseDefault();// 7. 反序列化std::cout << "----------------"<<std::endl;res->Deserialize(resstr);res->PrintResult();break;}}return 0;
}

这样客户端逻辑就写好了!!!

4 运行测试

我们进行一下简单的测试首先注意因为我们使用JSON库编译时要加入对应的编译动态库选项:

.PHONY:all
all:calserver calclient
calserver:ServerMain.ccg++ -o $@ $^ -std=c++14 -lpthread -ljsoncpp
calclient:ClientMain.ccg++ -o $@ $^ -std=c++14 -ljsoncpp
.PHONY:clean 
clean:rm -rf calserver calclient

编译之后我们来看运行效果:
在这里插入图片描述
参照对应的ASCII码表:

运算符ACSII码
+43
-45
*42
/47
%37
&38
*42
!33

可以验证结果是正确的!!!
这样网络计算机的小项目就完成了!!!

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

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

相关文章

微服务学习笔记之Docker

目录 认识Docker 安装Docker 安装yum工具 配置Docker的yum源 更新yum&#xff0c;建立缓存 安装Docker 启动并校验 配置镜像加速 Docker常见命令 命令 演示 给命令起别名 Docker数据卷 认识数据卷 数据卷常见命令 nginx的html目录挂载演示 数据卷挂载本地目录或…

Redis: 特点,优势,与其他产品的区别以及高并发原理

入门Redis概述 1 &#xff09;选择Redis是因为其高性能 因为 Redis 它数据存储的机制是存在内存中的&#xff0c;减少了传统关系数据库的磁盘IO它是单线程的保证了原子性&#xff0c;它还提供了事务&#xff0c;锁等相关的机制 2 &#xff09;Redis 环境安装配置 linux 或 d…

企业网盘能作为FTP替代产品吗?

在数字化办公日益普及的今天&#xff0c;企业对于文件存储、传输和协作的需求不断增长。传统的FTP协议虽然在文件传输领域有着不可替代的地位&#xff0c;但其在用户体验、安全性、协作功能等方面逐渐显得力不从心。企业网盘作为一种新兴的数据管理解决方案&#xff0c;正逐渐成…

LeetCode234. 回文链表(2024秋季每日一题 26)

给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;hea…

建立分支提交代码

git分支 git branch 产看当前分支 git branch -a 查看所有分支 git checkout 分支名 切换分支 git checkout -b 分支名 建立分支&#xff08;仅仅是在本地建立了&#xff0c;并没有关联线上&#xff09; git push --set-upstream origin 分支名 把本地分支推到先线上 gti add …

【计算机视觉】YoloV8-训练与测试教程

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 制作数据集 Labelme 数据集 数据集选用自己标注的&#xff0c;可参考以下&#xff1a…

Paper 0 | Visual Instruction Tuning

使用机器生成的指令跟踪数据对大型语言模型 (LLM) 进行指令调整已被证明可以提高新任务的零样本能力&#xff0c;但这个想法在多模态领域的探索较少。我们首次尝试使用纯语言 GPT-4 生成多模态语言图像指令跟踪数据。通过对此类生成的数据进行指令调整&#xff0c;我们引入了 L…

多智能体笔记本专家系统:集成CrewAI、Ollama和自定义Text-to-SQL工具

在这个项目中&#xff0c;我们的目标是创建一个由多智能体架构和本地大语言模型&#xff08;LLM&#xff09;驱动的个人笔记本电脑专家系统。该系统将使用一个SQL数据库&#xff0c;包含有关笔记本电脑的全面信息&#xff0c;包括价格、重量和规格。用户可以根据自己的特定需求…

unix中如何查询和修改进程的资源限制

一、前言 一个进程在运行时&#xff0c;会用到各种资源&#xff0c;比如cpu的使用时间、内存空间、文件等等。那么&#xff0c;一个进程能够占用多少资源呢&#xff1f;cpu使用的时间有多长&#xff1f;进程空间有多大&#xff1f;能够创建多少个文件&#xff1f;这个就是本文…

2024.9.24 数据分析

资料 111个Python数据分析实战项目&#xff0c;代码已跑通&#xff0c;数据可下载_python数据分析项目案例-CSDN博客 【数据挖掘六大项目实战】敢说这是全B站讲的最详细最通俗易懂的数据挖掘教程&#xff01;整整60集&#xff01;学不会来找我&#xff01;-数据挖掘、数据挖掘…

校园自助打印系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;店长管理&#xff0c;打印店管理&#xff0c;打印服务管理&#xff0c;服务类型管理&#xff0c;预约打印管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&…

用 Pygame 实现一个乒乓球游戏

用 Pygame 实现一个乒乓球游戏 伸手需要一瞬间&#xff0c;牵手却要很多年&#xff0c;无论你遇见谁&#xff0c;他都是你生命该出现的人&#xff0c;绝非偶然。若无相欠&#xff0c;怎会相见。 引言 在这篇文章中&#xff0c;我将带领大家使用 Pygame 库开发一个简单的乒乓球…

SPSS26统计分析笔记——3 假设检验

1 假设检验原理 假设检验的基本原理源于“小概率事件”原理&#xff0c;是一种基于概率性质的反证法。其核心思想是小概率事件在一次试验中几乎不会发生。检验的过程首先假设原假设 H 0 {H_0} H0​成立&#xff0c;然后通过统计方法分析样本数据。如果样本数据引发了“小概率事…

Krita连接comfyui报错缺少节点如何解决

介绍一下我用的版本&#xff1a; krita5.2.3 ComfyUI-aki-v1.3 首先&#xff1a;文件夹必须严格按照ComfyUI进行命名&#xff0c;我不知道这个是不是必须得&#xff0c;但是看官方的文档以及我解决这个问题的过程时&#xff0c;是这样的。 报错信息如下图(这个报错图…

航拍工程车辆识别检测数据集 yolo数据集 共650张

航拍工程车识别检测数据集 yolo数据集 共650张 2 工程车辆识别数据集&#xff08;Engineering Vehicle Recognition Dataset, EVRD&#xff09; 摘要 EVRD 是一个专门针对航拍视角下的工程车辆识别而设计的数据集&#xff0c;旨在提供一种标准的训练和评估平台&#xff0c;用…

玩手机数据集 8201张玩手机的照片,有对应的xml和txt文件,可以用于yolo训练

玩手机数据集 8201张玩手机的照片&#xff0c;有对应的xml和txt文件&#xff0c;可以用于yolo训练 玩手机数据集&#xff08;Phone Usage Detection Dataset&#xff09; 数据集概述 该数据集专为检测人们使用手机的行为设计&#xff0c;旨在帮助研究人员和工程师开发高效的…

Uniapp时间戳转时间显示/时间格式

使用uview2 time 时间格式 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 <text class"cell-tit clamp1">{{item.create_time}} --- {{ $u.timeFormat(item.create_time, yyyy-mm-dd hh:MM:ss)}} </text>

从零开始的软件开发详解:数字药店系统源码与医保购药APP

很多小伙伴们疑问&#xff0c;医保购药APP是如何开发的&#xff0c;今天我将从零数字药店系统源码开始为大家提供一条清晰的实现方案。 一、技术架构设计 在开发医保购药APP之前&#xff0c;首先需要明确技术架构。一般来说&#xff0c;APP的技术架构可以分为前端和后端。 1…

手写SpringMVC(简易版)

在上一篇博客中说到这里我们要进行手写SpringMVC&#xff0c;因此最好是将上一篇博客中的SpringMVC源码分析那一块部分搞懂&#xff0c;或者观看动力节点老杜的SpringMVC源码分析再来看这里的书写框架。 首先我们要知道对于一个完整系统的参与者&#xff08;即一个完整的web项…

目标检测系列(三)yolov2的全面讲解

YOLOv2&#xff08;论文原名《YOLO9000: Better, Faster, Stronger》&#xff09;作为该系列的第二个版本&#xff0c;对原始YOLO进行了显著的改进&#xff0c;进一步提高了检测速度和准确度。在精度上利用一些列训练技巧&#xff0c;在速度上应用了新的网络模型DarkNet19&…