计算机网络——应用层协议(1)

在这篇文章初识网络中,我介绍了关于计算机网络的相关知识,以及在这两篇文章中Socket编程和Socket编程——tcp,介绍了使用套接字在两种协议下的网络间通信方式。本篇文章中我将会进一步介绍网络中网络协议的部分,而这将会从应用层开始。

1. 应用层协议以及序列化的引入

我们知道两台机器能够在网络上进行通信,主要的原因就是两台主机都遵守网络协议,而我们也知道网络协议本质上就是一种约定,体现到计算机中那就是,网络协议就是一个结构化的字段。
就比如我们现在在一个聊天软件上发消息,那么这个消息会携带三个内容:发出消息的用户昵称、发出消息的时间以及消息内容,那我们要将这个数据在网络上从一台主机发送到另一台主机这三个部分是一个一个的发吗?现在看似好像合理,确实可以一个一个的往过发,但是假如发送的消息很多呢?接收方如何知道哪个昵称对应哪个消息内容以及时间,所以在发送这三个内容时,必定是以整体发送的,在计算机中我们可以将这三个内容放在一个结构化字段中:

struct Message
{char nickname[128];char msg[1024];char time[128];
};

然后我们把这个结构体声明在服务端和客户端中,这样当一个服务端进行转发消息时,服务端和客户端都能知道接收到的消息该如何使用。这就是处在应用层的网络协议。
但是这样的作法还是具有局限性,对于上面的这个结构体,它不具有跨平台性,比如Linux和Windows下这个结构体可能大小不一样,或者直接就是服务端所使用的语言和客户端使用的语言不一样,这导致客户端就根本不认识这个结构体,那么当客户端接收到这么一个报文时,在应用层根本无法使用这个报文,所以我们建议在将这种结构化字段在网络上发送时以字符串(字节流)的方式发送数据比如这个结构体我们可以这样"nickname-msg-time"中间以横杠分割,字节流在任何平台上都是可以被正确识别的,现在我们只要规定这个字节流如何读取(也就是制定好协议),那么服务端和客户端就算是不同平台也能正常进行网络通信了。
上面这种将结构化字段转化为字节流的过程叫做序列化,将字节流再转变为结构化字段时,叫做反序列化。
将网络上传输的数据在用户层中变成结构化字段的原因是为了便于用户处理这些信息。
而真正在网络上传输需要将结构化字段进行序列化是为了便于网络传输,以及支持跨平台。
需要注意的是,在网络协议的其他层中操作系统发送网络数据仍然是固定大小的结构体,而它不需要进行序列化然后再进行网络传输的原因是那几层协议很长时间才会迭代一次,而应用层协议的迭代是很快的(比如我现在就要往Message结构体中添加头像字段),所以我们需要通过使用序列化结构化字段来让网络传输变得更方便。
现在我将会使用tcp协议的套接字实现一个简易版的计算器,来更好的认识应用层协议,以及会引入更加规范的应用层协议的制定方法。

2. 套接字接口的封装

在此之前,我先要对套接字接口进行封装以为了更方便的使用它们(对tcp协议相关接口)。
我们知道在tcp协议下使用套接字接口会使用到这些接口:
在这里插入图片描述
我们也知道在服务端进行bind时时需要固定IP的,只需要一个端口号,客户端不需要显式bind,客户端会在connect时进行bind随机端口。
所以我们可以对这些接口进行封装:
在这里插入图片描述
在这里我们定义了一个Socket类,是一个抽象类,而我们将tcp所使用的接口设置为纯虚函数,让子类来进行实现,以黄框中的函数作为骨架,让整体的执行逻辑不变,而只在底部对纯虚函数进行重写。这样的设计模式就是模板方法类:它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。
所以我们只需要继承这个抽象类然后对其中的纯虚函数进行重写来实现我们的tcp套接字就可以了:
在这里插入图片描述
至此我们的tcp套接字接口就编写好了,接下来我们就可以开始构建服务端的服务了。

3. 服务端

作为使用tcp协议的服务端,我们应该有一个端口号和一个监听的socket文件:
在这里插入图片描述
现在就是我们的服务的主要逻辑了:
在这里插入图片描述
我们的新连接进入新线程之后,我们的连接中一定要有要执行的业务,那么这个业务我们想从TcpServer的外部也就是回调的方法,实现建立连接和提供给客户端服务进行解耦。而且提供服务我们的可执行业务一定要具备读写网络数据的能力,也就是得需要新连接的文件描述符,这里我们对文件描述符进行了封装所以我们这里需要将newsock传过去:
在这里插入图片描述
接下来我们就需要来编写服务端的主函数了:
在这里插入图片描述

4. 业务编写

接下来就是我们的业务编写了,而我想写一个简单的计算器的功能,客户端通过传输服务端两个操作数和一个运算符,之后服务端进行运算以及给客户端发送运算的结果。
为了两端能够正常通信,我们就需要指定协议了,而前面也说过制定协议,其实就是对通信过程中的信息做结构化处理,让两端都能认识对方发过来的是什么,那么现在开始我们就来编写一个简单的具有请求和相应的计算器功能:
在这里插入图片描述
客户端发送请求,然后服务端接收到请求,对这个请求进行处理,最后响应回客户端:
在这里插入图片描述

5. 客户端

在开始进行通信之前,我们先进行客户端的简单的编写:
在这里插入图片描述

c. 正式开始编写业务逻辑

1). 直接传结构化字段

我们在上面已经制定了一个不太完善的协议,我们先使用直接传结构体的方式,进行网络上的请求和响应 :

在这里插入图片描述
现在我们回到ServerHandler业务逻辑的编写:
在这里插入图片描述
这样服务端和客户端就可以通信了。
在这里插入图片描述
但是直接传结构化字段正如开始所说,他是有缺陷的,用结构化字段来直接作为报文的话,服务器和客户端的应用场景就很局限,只要客户端进行了跨平台(例如使用其他语言)的话,这个客户端就不能使用了,就算能发给服务器信息,服务器也不认识他发过来的是什么,因为客户端方是根本不认识C++中的结构体的,所以我们就需要将我们要传输的结构化字段序列化。再规范点的话就将他变成一个报文。

2). 较为规范的应用层协议

上面我们简单的写了一个客户端和服务端进行网络通信以计算器为基本业务的代码,其中通信方式是双方共同认识计算器结构体,以此作为应用层协议。但是这样的传输方式是有问题的,首先就是使用场景太局限,除此之外我们还有其他的问题,在此之前我们需要再次认识tcp中面对字节流,以及tcp协议中的一些更为细节的知识:

tcp协议中的细节知识

我们知道当服务端和客户端使用tcp协议建立连接之后,双方都会有一个用来通信的socket文件描述符,其实我们的tcp协议的通信方式所对应的通信的文件描述符底层是这样的:
在这里插入图片描述

我们的socket文件描述符在操作系统中有着两个文件缓冲区,一个用来接收网络消息,一个用来发出网络消息,所以我们的服务端在应用层所使用的write和send是发送给了客户端吗?其实并不是,write和send是以拷贝的形式拷贝给了发送缓冲区,然后再由操作系统发送给客户端的接收缓冲区,在说得清楚点,就是tcp协议决定着发送缓冲区中的数据发送给客户端,这其中的决定包括什么时候发?发多少?发送过程出错了怎么办?这一切都由tcp协议来做决定。所以我们使用tcp协议进行通信的时候,本质上其实是操作系统间进行通信。
我们也知道tcp协议是面向字节流的一种通信方式,这就意味着我们发送缓冲区的内容有多少发送到了客户端的接收缓冲区中,我们是不确定的(这一般是由客户端接收缓冲区的容量有关),就比如我们现在在应用层使用send发送了一个"hello world",我们将这段内容实际上是拷贝到了本主机的发送缓冲区中,然后可能此时客户端的接收缓冲区中只能容纳五个字节了,这个时候我们的tcp协议在将自己的发送缓冲区中的内容发给客户端的时候就只能发过去个hello:
在这里插入图片描述
这个时候我们的客户端将这个不完整的数据读上去,那么就会导致数据发生错误,这假如要是我们的上面的计算器结构体的话,这就会直接读取错误。虽然在大部分情况下不会出现这种情况,但是这种情况我们能确定一定不会发生(主要是当前网络压力太小)。
又或者我们的服务端发送了两次hello world,而客户端在服务端第一次发送的时候第一时间没读,而是后来一起读的,这也会导致我们读取的数据错误。
我们发现在使用tcp协议通信时,通信的消息没有明显的 “边界感” 这就是面向字节流的特性。
而udp协议就不需要担心这样的问题,因为udp协议是面向数据报的,它发送数据时,要么不发送要么就全部发送过去,并且在对方未进行读取前,发送端是不能再次进行发送的,发送端会被阻塞。数据和数据之间具有明显的 “边界感” ,这就是面向数据报。
所以为了解决跨平台、数据间没有边界感等问题,我们就需要制定比完善的用户层协议。
所以接下来我们就来写一个比较规范的用户层协议。

序列化和反序列化

首先我们的两端传输的数据不再是结构化字段,而是具有一定格式的字节流也就是字符串,我们叫做序列化和反序列化,所以我们需要在计算器中的添加序列化和反序列化的功能:
我们要清楚我们要序列化的内容是有效载荷,要反序列化的内容也是有效载荷:
并且这里我们约定,请求序列化格式是:

	// 中间用空格隔开// _data_x _oper _data_y

在这里插入图片描述

在这里插入图片描述

响应的序列化格式是

	// 中间也是空格隔开// _result _code

在这里插入图片描述
而我们请求反序列化代码如下:
在这里插入图片描述
响应反序列化:
在这里插入图片描述
现在我们就可以直接使用一下这个序列化和反序列化的代码了,在这个时候我写了一个关于计算器中的对象的工厂模式:
在这里插入图片描述
计算器请求中的Result函数也得修改:
在这里插入图片描述

现在我们来使用一下这个序列和反序列化:
客户端
在这里插入图片描述
服务端:
在这里插入图片描述
在这里插入图片描述
然后我们再将客户端的操作数等,使用随机数的方式来初始化操作数以及描述符:
在这里插入图片描述
再来看效果:
在这里插入图片描述
但是,还有问题,我们现在只解决了结构化字段序列化可以跨平台的功能,但是我们仍无法保证我们两端收到的信息是独立的且完整的,也就是网络通信的数据还是没有边界感,而现在我们传的数据实际上还只能算作有效载荷,所以我们要将数据发送时,变成一个较为规范的报文。

报文

我们规定报文的格式是这样的:

	// "len\n有效载荷\n"

也就是在有效载荷的前面添加一个字段len,这个字段用来表示有效载荷的长度,中间用特殊字符\n来隔开,因为我们能确保在读取这个报文的时候在len字段中一定是没有\n的存在的,它只有数字。其中len字段就是报文的自描述字段,这样规定的好处是,它不仅可以用来给计算器业务进行封装报头,它也可以给任意的有效载荷进行封装报头。
而有效载荷的后面的\n不属于有效载荷也不属于封装的报头,只是为了Debug方便。
那么现在我们就来编写对有效载荷进行封装以及对报文解包的代码:
在这里插入图片描述
在将这个模块 添加到我们的服务端和客户端时,我们需要对服务端的代码进行改造:
在这里插入图片描述
我们这段代码中它有两个功能,一个是收发网络信息的功能,一个是处理网络数据的功能,我们只想让它具有网络数据处理的功能,而将手收发网络数据的功能让外面的线程来做,所以:
在这里插入图片描述
在这里插入图片描述

TcpSocket中封装并重写recv和send:
在这里插入图片描述
ServerHandler函数:
在这里插入图片描述
在这里插入图片描述
线程函数优化:
在这里插入图片描述

客户端代码:
在这里插入图片描述
运行结果:
在这里插入图片描述
可以看到,我们现在的代码仍然能够正确运行,并且网络间传输的数据格式较为规范。这就是一个比较规范的应用层协议。

6. 引入较为成熟的应用层协议

在上面的代码中,我们通过自定义应用层协议使得网络通信变得较为规范,其实现在已经有一批比较成熟的应用层协议,比如使用较多的就是json。我们可以使用json来编写:
计算器请求序列化与反序列化:
在这里插入图片描述
响应序列化和反序列化:
在这里插入图片描述
代码运行结果:
在这里插入图片描述
json可以通过键:值字符串的方式来存储你想要存储的结构化信息,最后用花括号把所有内容包含起来,其中的 "值"可以是整形,字符串,数组甚至是json对象。
这就是关于网络协议中自定义应用层协议的全部内容。

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

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

相关文章

[SWPUCTF 2022 新生赛]ez_ez_unserialize

要绕过wakeup函数&#xff0c;只要序列化的中的成员数大于实际成员数&#xff0c;即可绕过。 <?php class X {public $x fllllllag.php; }$anew X(); echo serialize($a); O:1:“X”:1:{s:1:“x”;s:13:“fllllllag.php”;} 修改为 O:1:“X”:3:{s:1:“x”;s:13:“flllll…

【Java--数据结构】“从扑克到程序:深入探讨洗牌算法的原理与魅力“

前言 以下是学习Java顺序表的一个实例应用———简单的洗牌算法。 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 定义每张扑克牌的属性 生成一副扑克牌&#xff08;不包含大小王&#xff09; 洗牌方法 发牌方…

邂逅JavaScript逆向爬虫-------基础篇之深入JavaScript运行原理以及内存管理

目录 一、JavaScript运行原理1.1 前端需要掌握的三大技术1.2 为什么要学习JavaScript1.3 浏览器的工作原理1.4 浏览器的内核1.5 浏览器渲染过程1.6 认识JavaScript引擎1.7 V8引擎以及JavaScript的执行过程1.8 V8引擎执行过程 二、JavaScript的执行过程2.1 初始化全局对象2.2 执…

PCB上有哪些元素

过孔&#xff1a;是用来切换层的 丝印&#xff1a;就是标记&#xff08;白色的线或者符号&#xff09; 焊盘&#xff1a;焊接元器件&#xff0c;相当于线头&#xff0c;连接各个元件 通孔埋孔盲孔&#xff0c;都是用来换层&#xff0c;内部没有桐&#xff0c;是用来固定的 线路…

【pycharm】调试模式中四个常用按钮介绍

【pycharm】调试模式中四个常用按钮介绍 在 PyCharm 的调试模式中&#xff0c;有四个常用的按钮&#xff0c;它们的功能如下&#xff1a; Step Over (F8)&#xff1a;单步执行&#xff0c;但在遇到函数调用时&#xff0c;不会进入函数内部&#xff0c;而是将整个函数作为一步执…

从0到1—POC编写基础篇(二)

接着上一篇 POC常用基础模块 urllib 模块 Python urllib 库用于操作网页 URL&#xff0c;并对网页的内容进行抓取处理。 urllib 包 包含以下几个模块&#xff1a; ●urllib.request - 打开和读取 URL。 ●urllib.error - 包含 urllib.request 抛出的异常。 ●urllib.parse - …

【八股】计算机网络篇

网络模型 应用层【HTTP&#x1f449;报文/消息】 传输层【TCP或UDP&#x1f449;段&#x1f449;MSS】网络层【IP、寻址和路由&#x1f449;MTU】 ①IP&#xff08;Internet Protocol&#xff0c;网际协议&#xff09;主要作用是定义数据包的格式、对数据包进行路由和寻址&…

React-editor-js not showing up in a function component

React-editor-js not showing up in a function component react-editor-js 在react 函数组件中显示不出来 真的&#xff0c;我马上就想放弃它了。但是看它周下载量还挺多&#xff0c;我不信别人没遇到过。于是我继续在网络上挖呀挖。只是我一开始的方向错了。我一直以为我的写…

6.2 整合MongoDB

6.2 整合MongoDB 1. MongoDB简介2. MongoDB安装2.1 下载2.2 配置MongoDB2.3 MongoDB的启动和关闭1. 启动MongoDB2. 关闭MogoDB 2.4 安全管理 3. 整合SpringBoot3.1 依赖3.2 MongoTemplate使用3.3 测试1. 新增2. 查询3. 删除 *************************************************…

仓库管理存在的问题及改进对策?

大部分人都指导仓库问题会影响一个仓库操作或与之相关的整个流程链的速度、效率和生产力。但在大多数情况下&#xff0c;只有在流程开始甚至完成后才能识别这些错误。 到那时通常已经来不及阻止错误了&#xff0c;甚至可能来不及减少造成的损害。 所以这也是我写这篇内容的目…

[SWPUCTF 2021 新生赛]re2(不同字符加密相同,逆向修改范围)

无壳 直接看ida 完整exp&#xff1a; resultlist(ylqq]aycqyp{) for i in range(len(result)):if (ord(result[i])<94 or ord(result[i])>96) and (ord(result[i])<62 or ord(result[i])>64):result[i]chr(ord(result[i])2)else:result[i]chr(ord(result[i])-24)…

数据结构实验(三)

算法设计 一、判断回文序列 1、算法思路&#xff1a; 输入想要判断的字符串&#xff0c;用数组来存放该字符串&#xff0c;给数组一个最左的下标low,和最右的下标right.比较两端的字符是否相等&#xff0c;如果相等那么low,right--.直到遍历完字符串&#xff0c;如果字符不相…

可持续发展:制造铝制饮料罐要消耗多少资源?

铝制饮料罐是人们经常使用的日常用品&#xff0c;无论是在购物、午休还是在自动售货机前选择喝什么的时候&#xff0c;很少有人会想知道装他们喝的饮料的罐子到底是如何制成的&#xff0c;或者这些铝罐的原材料是如何进出的。 虽然有化学品和一些合金进入铝饮料罐制造过程或成为…

大小端解释以及如何使用程序判断IDE的存储模式

今天让我们来了解一下大小端的概念吧 什么是大小端&#xff1f; 大端&#xff08;存储&#xff09;模式&#xff1a;指的是数据的低位保存在内存的高地址处&#xff0c;而数据的高位则保存在内存的低地址处。 小端&#xff08;存储&#xff09;模式&#xff1a;指的是数据的低位…

在 Windows 系统上彻底卸载 TeamViewer 软件

在 Windows 系统上彻底卸载 TeamViewer 软件 References 免费版仅供个人使用 您的会话将在 5 分钟后终止 Close TeamViewer by locating the TeamViewer icon in the system tray, right click and “Exit TeamViewer”. Right click Windows start menu then Control Panel -…

“PowerInfer:消费级GPU上的高效大语言模型推理引擎“

PowerInfer是由上海交通大学IPADS实验室开发的一个高效大语言模型&#xff08;LLM&#xff09;推理引擎&#xff0c;专为个人电脑&#xff08;PC&#xff09;上的消费者级GPU设计。它通过利用LLM推理中的高局部性&#xff0c;实现了快速且资源消耗低的模型推理&#xff0c;这一…

阿里二面凉了,难蹦。。。

分享一位同学阿里巴巴的后端面经&#xff0c;共有 2 面&#xff0c;第一面很顺利过了&#xff0c;可惜挂在第二面。 这两面的知识点范围&#xff0c;我帮大家罗列一下&#xff1a; 网络&#xff1a;TCP、HTTP mysql&#xff1a;索引应用、索引结构、隔离级别、最左匹配 redis…

数据分析专家能力模型

招式&#xff1a;懂商业&#xff08;业务能力&#xff09; 外功更偏重于技能&#xff0c;首先需要懂招式&#xff0c;即懂商业&#xff0c;数据分析最终是为业务服务的&#xff0c;无论是互联网企业准求的用户增长和UJM分解&#xff0c;还是传统企业追求的降本增效和精细化运营…

图像处理之Retinex算法(C++)

图像处理之Retinex算法&#xff08;C&#xff09; 文章目录 图像处理之Retinex算法&#xff08;C&#xff09;前言一、单尺度Retinex&#xff08;SSR&#xff09;1.原理2.代码实现3.结果展示 二、多尺度Retinex&#xff08;MSR&#xff09;1.原理2.代码实现3.结果展示 三、带色…

Axure设计美观友好的后台框架页

使用Axure设计后台框架页 优点介绍&#xff1a; **1、使用中继器灵活配置菜单项&#xff1b; 2、二级菜单面板跟随一级菜单位置显示&#xff1b; 3、菜单链接打开后&#xff0c;联动添加tab标签&#xff1b; 4、标签页与iframe内容联动&#xff0c;可关闭&#xff1b; 5、左侧…