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

news2024/11/15 11:28:32

在这里插入图片描述

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

从零开始掌握序列化

  • 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. 构建request
        auto 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.cc
	g++ -o $@ $^ -std=c++14 -lpthread -ljsoncpp
calclient:ClientMain.cc
	g++ -o $@ $^ -std=c++14 -ljsoncpp
.PHONY:clean 
clean:
	rm -rf calserver calclient

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

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

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

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

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

相关文章

CSS 实现文本溢出省略号显示,含单行与多行文本溢出

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

[翟旭发射器]python-推导式-列表list表达式练习

# 简单的列表生成 numbers00[x for x in range(1,11)] print(numbers00) # 带条件的列表生成 numbers01[x for x in range(1,11) if x%20] print(numbers01) # 带表达式的列表生成 numbers10[x**2 for x in range(1,11)] print(numbers10) # 嵌套循环的列表生成 coordinates[(x…

UE4_Niagara基础实例—使用自定义参数

实现的功能&#xff1a;使用自定义的参数来调整粒子远离发射器后粒子大小的变化 效果图&#xff1a; 操作步骤&#xff1a; 1、创建Niagara系统&#xff0c;使用Simple Sprite Burst模板作为新系统的发射器&#xff0c;更名为NS_Custompara。 2、双击打开Niagara系统编辑界面…

Shopee 大促想爆单如何准备?EasyBoss ERP为你准备了一份攻略!

Shopee下半年第二个大促节点——10.10品牌大促即将来到&#xff0c;根据Shopee的官方的数据&#xff0c;9.9大促当天&#xff0c;Shopee Mall单量增至平日4倍。 老板们&#xff0c;准备好自己的热卖爆款冲击10.10大促了吗&#xff1f; 图源&#xff1a;Shopee 为助力大家迎战大…

NetApp EF 系列全闪存阵列 EF600 和 EF300

功能强大且经济实惠的性能 NetApp EF600 全闪存阵列专为需要最高性能的工作负载而设计。NetApp EF300阵列专为大数据分析和数据库等混合工作负载环境而设计。这些NVMe全闪存阵列的性能是以前SAS全闪存阵列的两倍。您可以使用专为高性能工作负载打造的端到端 NVMe 存储平台来加速…

node-rtsp-stream、jsmpeg.min.js实现rtsp视频在web端播放

1. 服务地址&#xff08;私有&#xff09;&#xff1a;https://gitee.com/nnlss/video-node-server 2.node-rtsp-stream 需要安装FFMPEG&#xff1b; 3.给推拉流做了开关&#xff0c;可借助http请求&#xff0c;有更好方式可联系&#xff1b; 4.存在问题&#xff1a; 1&…

Vue中集中常见的布局方式

布局叠加 完整代码最外层的Container设置为relative&#xff0c;内部的几个box设置为absolute <template><div class"container"><div class"box box1">Box 1</div><div class"box box2">Box 2</div><d…

导出Power bi中的Measure

工作中做交接文档时&#xff0c;有时需要导出Power BI中所有度量值&#xff0c;这时可以下载两个工具。 一 &#xff0c; 下载工具&#xff1a; 1 &#xff0c; DaxStudio&#xff1a; http://daxstudio.org/ 2&#xff0c; Vertipaq Analyzer&#xff1a; https://www.sqlbi.c…

1.1 elasticsearch分布式集群基本搭建(centos7.x + elaticsearch7.11.1)

【1】分布式分片集群基础概念 【1.1】ES的分布式集群有什么用&#xff1f; 高可用 高可用(High Availability)是分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计减少系统不能提供服务的时间。如果系统每运行100个时间单位&#xff0c;会有…

半导体供应链系统在智能时代的主要作用

在智能科技日新月异的今天&#xff0c;半导体作为信息技术的基石&#xff0c;其供应链系统的稳定与高效直接关系到全球科技产业的脉动。智能时代的浪潮下&#xff0c;半导体供应链系统不仅是技术的输送带&#xff0c;更是推动产业创新、促进经济转型升级的关键力量。本文我们就…

将Uni.app打包好的apk放到IIS服务器上的详细流程

第一步&#xff1a;打开IIS服务器&#xff0c;点击MIME类型 第二步&#xff1a;点击添加 第三步&#xff1a;文件扩展名填写&#xff1a;.apk &#xff0c; MIME 类型填写&#xff1a;application/vnd.android.package-archive 第四步&#xff1a;添加成功后重启IIS服务器 第五…

【大模型实战篇】一种关于大模型高质量数据的处理方法-无标注数据类别快速识别及重复数据检测(加权向量-卷积神经网络-聚类算法结合)

1. 背景介绍 大模型的能力很大程度上依赖于高质量的数据&#xff0c;在之前的一篇文章《高质量数据过滤及一种BoostedBaggingFilter处理方法的介绍》中&#xff0c;我们介绍了大模型的数据处理链路&#xff0c;本文继续关注在高质量数据的模块。 本文所要介绍的处理方法&…

C语言课程设计题目(24个选题)

C语言课程设计题目 题目一&#xff1a;职工信息管理系统设计题目二&#xff1a;图书信息管理系统设计题目三&#xff1a;图书管理系统设计题目四&#xff1a;实验设备管理系统设计题目五&#xff1a;西文下拉菜单的设计题目六&#xff1a;学生信息管理系统设计题目七&#xff1…

R包:gplots经典热图

加载R包 # install.packages("gplots")library("gplots")数据 mat <- matrix(rnorm(1200), ncol6)画图1 heatmap.2(xmat)画图2 heatmap.2(xmat, ColvFALSE, dendrogram"row",scale"row",col"bluered",trace"non…

用Python提取PDF表格到Excel文件

在对PDF中的表格进行再利用时&#xff0c;除了直接将PDF文档转换为Excel文件&#xff0c;我们还可以提取PDF文档中的表格数据并写入Excel工作表。这样做可以避免一些不必要的文本和格式带来的干扰&#xff0c;获得更易于分析和处理的表格数据&#xff0c;并方便进行更多的格式设…

uniapp监听滚动实现顶部透明度变化

效果如图&#xff1a; 实现思路&#xff1a; 1、使用onPageScroll监听页面滚动&#xff0c;改变导航条的透明度&#xff1b; 2、关于顶部图片的高度&#xff1a; 如果是小程序&#xff1a;使用getMenuButtonBoundingClientRect获取胶囊顶部距离和胶囊高度&#xff1b; 如果…

List几种遍历方法速度

准备数据 ArrayList<Integer> list new ArrayList<Integer>();for (int i 0; i < 100000000; i) {list.add(i); }1.for循环 for (int i 0; i < list.size(); i) {Integer str list.get(i); }2.for-each循环 for (Integer vo:list) {}3.Lambda list.forEa…

XXl-SSO分布式单点登录框架

概述 下载地址&#xff1a;https://gitee.com/xuxueli0323/xxl-sso 文档地址&#xff1a;https://www.xuxueli.com/xxl-sso/ 概述 XXL-SSO 是一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、CookieToken均支持…

工业数据采集系统

一、网页部分代码 效果图&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…

中国蚁剑(antSword)安装使用

antSword下载 antSword-Loader下载 作者&#xff1a;程序那点事儿 日期&#xff1a;2024/09/12 19:35 中国蚁剑&#xff08;AntSword&#xff09;是一款跨平台的开源网站管理工具&#xff0c;旨在满足渗透测试人员的需求。它是一个功能强大的工具&#xff0c;可以帮助用户管理…