浅谈网络通信(2)

文章目录

  • 一、TCP
    • 1.1、TCP提供的api —— ServerSocket 、Socket
    • 1.2、使用TCP协议编写回显服务器
    • 1.3、长/短连接
  • 二、应用层协议、传输层协议详解
    • 2.1、应用层(后端开发必知必会)
      • 2.1.1、自定义应用层协议
      • 2.1.2、通用的协议格式
        • 2.1.2.1、XML
        • 2.1.2.2、json
        • 2.1.2.3、protobuffer
    • 2.2、传输层
      • 2.2.1、UDP

一、TCP

TCP服务器端需要做的事:
1、获取连接
2、处理连接:(1)、读取请求并解析 (2)、根据请求计算出响应 (3)、写回响应
TCP客户端需要做的事:
1、构造请求并发送
2、从服务器端获取响应
3、把响应显示

1.1、TCP提供的api —— ServerSocket 、Socket

1、ServerSocket:
ServerSocket 是提供给服务器使用的类。
由于TCP特点是有连接的,因此TCP服务器启动后的第一件事不是读取客户端请求,而是先处理客户端的 “连接”。

握手是由系统内核负责的,写代码过程中无法感知到握手的过程,写代码时主要是处理连接,连接是握手之后得到的东西,是比较抽象的,就是说客户端和服务器彼此保存了对方的信息,这个抽象的连接是由系统内核完成的)。

一个服务器要对应很多的客户端,因此内核中的连接很多,这些连接就像一个一个的待办事项,这些待办事项存储在一个队列这样的数据结构中,应用程序就需要从队列中一个一个的取出连接,完成这些连接任务。

2、Socket:
既会给服务器使用,也会给客户端使用。

TCP传输数据的基本单位是 字节流,一个TCP数据报,就是一个字节数组 byte[] buf。

1.2、使用TCP协议编写回显服务器

使用TCP协议编写回显服务器的源码地址
代码详解:
1、serverSocket.accept():把内核中的连接获取到应用程序中。(但是此处的返回值并非是一个 “Connection” 这样的对象,而是一个 Socket 对象,这个 Socket 对象,就像一个耳麦一样,拿着它既可以讲话让对方听见,也可以听到对方讲话的声音(是TCP全双工的表现))。
这个过程类似于 “生产者消费者模型”。

在这里插入图片描述
但有可能出现一种情况:即当程序执行到代码serverSocket.accept()时,有可能此时服务器还没有客户端连接,那么此时队列中也就没有任何连接,应用程序也就从内核中取不到任何连接,此时程序就会阻塞在accept()方法这里,直到有客户端连接成功为止。(一次IO分两个部分:1、等待(阻塞)。2、拷贝数据)

为啥建立连接要进行握手??就像相亲时不可能双方一见面就马上结婚(结婚也是一种抽象的连接),必须有一个相互了解的阶段。此处衍生出一个重要的面试题:TCP的3次握手和14次挥手。(后面再详解)

2、在TCP服务器代码处,有两种 Socket 对象,一种是 ServerSocket ,一种是 Socket 。
在这里插入图片描述
那这两种 Socket 有什么区别?
serverSocket 就像面包店外试吃区的销售人员,socket 就像面包店里售卖面包的销售人员。试吃区的销售人员其实就是在为面包店拉客,面包店里的销售人员会给被试吃区拉来的顾客详细介绍店里的面包种类,来进一步吸引顾客购买。这两种销售人员虽然有不一样的称呼,但是他们的目的都是为了销售面包店里的面包给顾客。

ServerSocket 只有一个,生命周期跟随程序,不关闭也没事。但是对于 Socket 来说,一定要关闭。如果有1w个客户端连接,就会有1w个Socket,此处的 Socket 是被反复创建的,因此对于 Socket 来说,必须确保连接断开之后会被关闭!否则就会发生文件资源泄露,导致整个程序被带走。

3、在TCP服务器代码中,通过方法 processConnection() 处理客户端的一个连接,由于accept() 方法会阻塞,因此需要借助线程池来处理客户端的连接。当只有一个执行流时,accept()方法处发送阻塞会导致其他连接了服务器的客户端没办法继续执行,这显然不符合一个服务器的执行常理,因此服务器端需要借助线程池的多个执行流处理客户端的连接。

在这里插入图片描述
其实我们的服务器代码没加上线程池前,会出现一个问题:当第一个客户端连接好了之后,第二个客户端,不能正确被处理:即此时服务器看不到客户端上线,同时客户端发来的请求也无法被处理。当第一个客户端退出之后,之前第二个客户端发来的请求,就能正确的响应了。

在这里插入图片描述
但是对于服务器来说,服务器是能够同时处理多个客户端的请求,因此上述第一个客户端连接执行完毕后,第二个客户端连接才能被处理这种做法是不正常的,因为一个客户端连接不知道什么时候才能执行完,一个客户端连接是可以进行很多个请求、响应的,因此对应的也就不知道accept()方法的阻塞什么时候才能结束,因此想要解决accept()的阻塞,可以使用线程池来解决该问题,使用线程池后程序中就可以含有多个执行流,既可以执行accept(),也可以执行processConnection()方法。一个执行流执行processConnection()方法,另一个执行流也能快速调用到accept()。不使用线程池也可以,只要是多线程方式都可以解决该问题。(在网络编程中,多线程会常常使用到)

还有一个问题
有些同学可能会在TCP服务器端写出这样的代码:
在这里插入图片描述

还有问题:我们引入了 线程池 解决了 accept() 阻塞问题,但是线程池也是有开销的,在服务器对应的客户端很多的情况下,服务器就需要创建出大量的线程去处理客户端连接的任务,此时服务器的开销是很大的,响应速度也会大打折扣。那么是否有办法,使用一个线程或者3、4个线程就能让服务器高效的处理客户端的并发(几万/几十万客户端)请求?

其实随着互联网的发展,客户端会越来愈多,请求也越来越多,C10M就出现了。C10M:同一时刻,有1kw的客户端并发请求。此时服务器的负担真的很大,因此引入了很多技术,其中一个很有效的手段就是:IO多路复用!(一种节流的方式,在同样的请求下,消耗的硬件更少了,本质上就是减少线程的数量)。

解决高并发,其实就是两步走:1、开源:引入更多的硬件资源。2、节流:提高单位硬件资源能够处理的请求数,同样的请求数,消耗的硬件资源更少。

4、String request = sc.next();
此处使用 next() 读取数据,一直读到空白符算结束。那什么是空白符?包括但不限于:换行符(\n:让光标另起一行)、回车符(\r:让光标回到行首(不会另起一行))、空格键、制表符、换页符、垂直制表符…

5、PrintWriter

在这里插入图片描述
访问IO比访问内存的开销大,因此进行IO的次数越多,程序的速度越慢。因此通常会使用一块内存作为缓冲区,写数据的时候,先写到缓冲区里,缓冲区里攒一波数据,统一进行IO。而 PrintWriter 内置缓冲区。printWriter.flush() 是进行手动刷新,就是确保缓冲区里的数据真的通过网卡发出去了,而不是残留在内存缓冲区里,缓冲区内置了一定的刷新策略,例如缓冲区满了,就会触发刷新,例如程序退出,也会出发刷新。

6、
在这里插入图片描述
流对象中持有的资源,有两个部分:1、内存(对象销毁,内存就会被回收)。2、文件描述符。

当 while 循环一圈,内存自然就会被销毁。 在我们服务器端的代码,Scanner 和 PrintWriter 没有持有文件描述符,他们持有的是 inputStream、outputStream 的引用,当 inputStream、outputStream 被销毁之后,Scanner 和 PrintWriter 也都被销毁了;而 inputStream、outputStream 是被 Socket 对象持有的,因此当 Socket 对象被关闭之后,inputStream、outputStream 也就随之被销毁了。

1.3、长/短连接

TCP程序时,涉及到两种写法:1、短连接:一个连接中只传输一次请求和响应。2、长连接:一个连接中可以传输多次请求、连接。

二、应用层协议、传输层协议详解

2.1、应用层(后端开发必知必会)

2.1.1、自定义应用层协议

应用层这一层,有很多现成的协议,也有很多时候,需要我们程序员自己去自定义应用层协议。自定义应用层协议,也是一件很简单的事情。

举个例子:此处有一个需求场景:一个外卖软件,需要在用户打开此软件时,给用户显示用户住址附近的商家列表,列表中有很多项,每一项都包含了一些信息:譬如:商家名称、商家图片、商家店铺好评率,商家与用户的距离、商家评分…(其实外卖软件和服务器之间的沟通,有很多种方式,展示商家列表,只是其中之一)

客户端(用户…):需要给服务器发起一个请求,服务器收到请求之后,就给客户端返回一个响应。[!]那么这个请求里应该包含什么信息呢??该请求应该按照什么格式组织呢??同样的,这个响应应该包含什么信息??应该按照什么格式解析??

此时程序就应该做出如下设计:
1、明确当前程序的请求和响应中都包含哪些信息??(一般在实际开发工作中,请求和响应中都包含哪些信息是根据需求文档里标的来写的)
2、明确具体的请求和响应的格式。[所谓的“明确格式”就是看你按照啥样的方式构造出一个字符串,后续这个字符串就可以作为 tcp/udp的payload进行传输]

明确具体的请求和响应的格式:
示例1:假设此处的请求包含的信息:用户身份、用户当前位置;响应包含的信息:商家名称、商家图片、商家好评率、商家距离用户的位置、商家评分…
请求:1234,80 100\n
响应:蓝胖子肉蟹煲,1.jpg,98%,1km,4.9\n 美蛙鱼头,2.jpg,95%,2km,4.2\n \n
示例1 的响应使用\n来分割每条商家信息。

示例2:
请求:1234;80,100.
响应:蓝胖子肉蟹煲;1.jpg;1km;98%;4.7!美蛙鱼头;2.jpg;2km;95%;4.2!.
示例2 的响应使用!来分割每条商家信息。
其实我们可以看到示例2的商家列表的信息顺序跟示例1并不一样,这是因为这些都是可以自定义的,不需要一样。

示例3:
在这里插入图片描述
从上述几种示例可知,请求和响应的数据组织格式非常灵活,程序员想怎么组织就怎么自定义,当然了,自定义的时候必须要能够保证客户端和服务器这边使用的是相同规则即可。因为在实际开发中,客户端和服务器,往往是由不同的人员负责,因此开发之前,双方就必须共同约定好请求和响应的数据组织格式。

虽然说程序员可以自定义应用层协议,但是为了避免出现过于天马行空的设计,此时行业标准就规定了一些“通用的协议格式”,程序员开发时参考这些格式,就可以在自定义应用层协议时更加严谨、正确。

2.1.2、通用的协议格式

2.1.2.1、XML

xml:是以成对的标签来表示键值对的信息,标签支持嵌套,就可以构成一些更复杂的树形结构数据。
以上面的外卖软件例子为例,使用xml格式来表示 请求:

<request>
   <useId>1234</useId>
   <position> 80 100</position>
</request>

响应:

<response>
   <shops>
      <shop>
        <name>蓝胖子肉蟹煲</name>
        <image>1.jpg</image>
        <distance>1km</distance>
        <rate>98%</rate>
        <star>4.7</star>
      </shop>
    
      <shop>
        <name>美蛙鱼头</name>
        <image>2.jpg</image>
        <distance>2km</distance>
        <rate>95%</rate>
        <star>4.2</star>
      </shop>
   </shops>
</response>

上面的 xml 格式表示的是 键值对 结构。key:userId,value:1234。其实对象本质上也是键值对,属性的名字就是 键,属性的值就是 值。

xml 优缺点:
优点:非常清晰的将结构化数据表示出来了。
缺点:表示数据需要引入大量的标签,看起来繁琐,同时也会占用不少的网络带宽。

其实我们有没有觉得 xml 的编写形式和 html 有些类似,html是编写网页的语言,也是以标签的形式出现。但是 xml 里的标签是程序员自定义的,html 里的标签是有自己的一套使用标准的。(可以把 html 视为是 xml 的特化版本)

2.1.2.2、json

json,当前最主流的一种网络传输数据的格式,本质上也是键值对,但json看起来,比 xml格式 简单便捷、干净很多;json对于换行并不敏感,如果这些内容全都放在一行,也是完全合法的。

一般网络传输时,会对json进行压缩(即去掉不必要的换行和空格),同时把所有数据都放到一行去,要传输的数据其整体占用的带宽就降低了。但可能这样的压缩会影响可读性,不过可以使用一些现成的 json 格式化工具恢复json原有的格式。

在json中,使用 { } 表示键值对,使用 [ ] 表示数组,数组里的每个元素,可以是数字、字符串、字母、或者是其他的 { } 、[ ]…

json格式的请求:
{
userId:1234,
position:“100 80”
}

响应:
{
{
name:“蓝胖子肉蟹煲”
image:“1.jpg”,
distance:“1km”,
rate:98%,
star:4.7
}

{
name:“鱼头”
image:“2.jpg”,
distance:“2km”,
rate:95%,
star:4.2
}
}

json优点:相比于 xml ,表示的数据简洁很多。

2.1.2.3、protobuffer

protobuffer是谷歌提出的一套二进制数据序列化方式,使用二进制的方式,约定几个字节,表示哪个属性。

优点:可以最大程度上节省空间,节省带宽,最大化效率。即不必传输 key,可以根据位置和长度,区分每个属性。
缺点:是二进制数据,无法直接肉眼直接观察,不方便调试。

这类格式很有可能会在工作中用到,尤其是在一些规模复杂的后端服务器。

Java标准库提供好了上述3种序列化方式(数据组织格式),而其他的第3方库,提供的方式更加丰富,后续再介绍。

2.2、传输层

传输层最常见的协议是 UDP、TCP,学习一个协议,不仅要掌握协议的特性,还需要理解协议报文格式

2.2.1、UDP

在这里插入图片描述
我们都知道 UDP 载荷部分里是应用层数据报,那么UDP报头里都有啥,都是啥信息??[重点理解]
在这里插入图片描述
从上图知:一个 UDP报头 含有4部分:1、源端口号。2、目的端口号。3、UDP报文长度。4、校验和。这4个部分都是2个字节的长度,那2个字节,表示的数据范围有多大(这可是最基本的知识点,必须牢记):
(1)、1字节:
有符号:-128 ~ +127
无符号:0~255
(2)、2字节:
有符号:-32768 ~ +32767
无符号:0~65535
(3)、4字节:
有符号:-21亿 ~ +21亿
无符号:0~42亿9千万

在程序中,程序员设定某个这些基本单位时,一定一定要小心,不然程序极易出现错误:

例子1:假如一家游戏公司,使用单位为2字节的某个字段实现游戏的某个功能,我们知道2字节可以表示的数据范围为:0~65535,那么用户在玩游戏时,疯狂玩,此时其收获的成绩峰值就极速上升,上升至了6w,用户还不满足,希望自己的战绩能更好,再继续疯狂玩,但却突然发现,自己的战绩却从6w跌至0。这是不应该的,表示此时程序出现了bug。其实原因就是因为使用了2字节的字段作为表示用户战绩情况,2字节的数据表示范围:0 ~ 65535,当达到最大值之后,数值就无法再继续增大了,会从0重新开始。因此,程序中使用某个字段表示某个功能时,其字段的选择应该要慎重、要考虑其缺陷性。

例子2:假如我们在工作中,被安排了一个任务:记录这一天,公司的搜索浏览器一共有多少请求?假设公司搜索浏览器每天的请求量在10亿量级,此时记录请求的变量,是使用 int 还是 long 类型??

注意:10亿量级,是只有10亿吗??量级此单位,其实是2倍,10亿量级,其实是21亿多。那么此时,变量使用int就不合适了,因为int能够表示的数据范围是 0~65535,变量可以使用long类型,long类型的数据表示范围为 0 ~ 42亿9千万,此时该数据范围完全可以表示 21亿 。

报文长度:
UDP报文长度2个字节,通过换算单位,可得到64kB,因此一个 UDP数据报其最大长度就是64KB(2个字节 = 216,210 = 1KB, 1KB*2^8 = 64KB),那如果说,当前程序使用UDP协议通信,程序想发送的数据超过了64KB,此时怎么使用UDP数据报来携带已超过协议最大长度的数据进行网络传输?1、可以把数据拆分成多组,通过几个UDP数据报进行传输;2、使用 TCP 代替 UDP ,因为 TCP 没有长度限制。不管用啥办法,反正UDP就是不能超过它的最大长度。因为当时行业标准设定UDP的最大长度就是64KB,我们只能遵守这个规则。

源端口、目的端口:
UDP端口号长度2个字节:可以看到 UDP报头 包含 源端口和目的端口,因为端口号使用2字节表示,因此端口号的数据表示范围为0~65535,但对于 1 ~ 1024 的端口号范围,这个范围的端口号已经被一些知名的程序的服务器占用,因此我们写的服务器,很少会用1 ~ 1024 范围的端口号来表示了,一般都是使用 1024之后的端口号表示,不过 1024 之后的端口号也有一些已经被知名程序的服务器占用了,如 3306 已经被 MySQL 程序占用了。

校验和:
其实在网络传输中,受到外界的干扰,传输的数据很容易出错,因为数据传送过程中,本质上是 光信号/电信号/电磁波,这些形式很容易受到磁场、高能粒子射线的干扰。此时就会导致本来你要传输的数据发生比特翻转,如 0 ——> 1、1 ——> 0…此时接收方接收到的数据就是错的。尤其是UDP的长度是64KB,遇上大过64KB的数据就需要进行拆分,将数据分成多组UDP数据发送,此时拆分的数据虽然说是传输过去了,但是对于接收方来说,还需要将数据拼接起来,此时怎么拼接?拼接过程发生了差错怎么办??怎么校验差错??那么,此时协议就通过 校验和 来对网络传输过来的数据进行校验,查看当前数据是否出错。实际的校验和,是会根据数据内容来生成,因此当数据出错,校验和就能感知到数据出错。校验和使用的是CRC校验算法(循环冗余校验和):即把UDP数据报中的每个字节都依次进行累加,把累加结果保存到2个字节的变量中,加着加着,可能就溢出了,但溢出也无所谓,所有字节都加了一遍,最终就得到了校验和。其实在传输数据时,就会把原始数据和校验和一起传递过去,接收方收到数据,同时也收到了发送端传送过来的校验和(旧的校验和),接收方按照同样的方式再算一遍,得到新的校验和。如果新的校验和 与 旧的校验和 相同,则可以视为数据传输过程中,数据是正确的,未发生差错;否则则反之。但是需要注意:数据相同,可推出校验和相同;校验和相同,无法推出数据相同!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国产数字证书大品牌——JoySSL

一、品牌介绍 网盾安全旗下品牌JoySSL是专业的https安全方案服务商&#xff0c;业务涉及网络安全技术服务、安全防护系统集成、数据安全软件开发等。网盾安全以网络安全为己任&#xff0c;携手GlobalSign、DigiCert 、Sectigo等全球数家权威知名SSL证书厂商&#xff0c;加速ht…

8-1RT-Thread消息队列

8-1RT-Thread消息队列 消息队列又称队列&#xff0c;是一种常用于线程间通信的数据结构。 消息队列控制块里有两个链表&#xff0c;空闲列表用来挂接空的小几块&#xff0c;另一个链表是用来挂接存有消息的消息框。其中消息链表头指向消息队列当中的第一个消息框&#xff0c;而…

ATA-3080C功率放大器在电解液体浸润性测试中的应用

现在的电子设备上的供电电池多为可反复充放电的锂电池&#xff0c;这种在我们日常生活中扮演着重要角色的电池&#xff0c;却有着自燃、爆炸的风险&#xff1b;随着电池在生活中的普及&#xff0c;电池检测相关行业和领域也随之发展。那么功率放大器在电解液体浸润性测试中有什…

自动求导实现与可视化

前言 micrograd为一个自动梯度引擎&#xff0c;其实现了反向传播算法&#xff0c;用于学习理解深度学习中的自动求导原理。自动求导无论再传统的机器学习中还是深度学习或是目前非常热门的大语言模型GPT中其都是非常重要基础部分。 反向传播算法可以高效计算出神经网络中损失…

护眼灯到底有没有用?警惕商家的四大智商税套路!

随着科技进步与大众健康意识的普遍提高&#xff0c;智能小家电逐渐成为了我们日常生活的一部分。在这些小家电中&#xff0c;一款被称为护眼台灯因其出色的护眼效果而备受瞩目。许多人好奇&#xff0c;护眼灯到底有没有用&#xff1f;是真的能够起到护眼效果的吗&#xff1f;而…

【设计模式深度剖析】【6】【行为型】【中介者模式】

文章目录 中介者模式定义英文原文直译如何理解&#xff1f; 中介者模式的角色1. 中介者&#xff08;Mediator&#xff09;2. 具体中介者&#xff08;ConcreteMediator&#xff09;3. 同事&#xff08;Colleague&#xff09;类图代码示例 中介者模式的应用优点缺点使用场景 中介…

【区块链】解码拜占庭将军问题:区块链共识机制的哲学基石

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 解码拜占庭将军问题&#xff1a;区块链共识机制的哲学基石引言一、拜占庭将军问…

微软云计算[2]之微软云关系数据库SQL Azure

微软云关系数据库SQL Azure SQL Azure概述SQL Azure关键技术SQL Azure数据库SQL Azure报表服务SQL Azure数据同步 SQL Azure和SQL Server对比 SQL Azure概述 SQL Azure是微软的云中关系型数据库。 SQL Azure数据库简化了多数据库的供应和部署。 SQL Azure还为用户提供内置的高…

meilisearch的Managing API keys,自己趟过的坑

Elasticsearch 做为老牌搜索引擎&#xff0c;功能基本满足&#xff0c;但复杂&#xff0c;重量级&#xff0c;适合大数据量。 MeiliSearch 设计目标针对数据在 500GB 左右的搜索需求&#xff0c;极快&#xff0c;单文件&#xff0c;超轻量。 所以&#xff0c;对于中小型项目来说…

graphpad加截断线 更改图表类型

1. 双击图表进入 2.设置最大值和最小值 设置的时候先设置bottom再设置top&#xff0c;否则改变不成功&#xff01;&#xff01; 3.设置坐标轴间隔 4. 更改图表类型

金融上云及信创改造过程中的新老设备兼容性、虚拟化多池管理简化、提升故障恢复能力等问题及解决方案|金融行业数字化QA合集②

Q&#xff1a;金融机构如何解决新老设备间的兼容性问题&#xff1f; 我行在虚拟化资源池扩容时&#xff0c;新采购的服务器与原有的服务器存在代差&#xff0c;容易出现新服务器的CPU架构与原有服务器不同&#xff0c;可能导致虚拟机迁移或运行时的性能问题或不兼容&#xff1…

AutoKG:为语言模型打造高效自动化知识图谱

在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;如BERT、RoBERTa、T5和PaLM等&#xff0c;以其在自然语言处理&#xff08;NLP&#xff09;任务中的卓越性能而著称。然而&#xff0c;这些模型在提供信息时可能会产生“幻觉”&#xff0c;即提供看似合理但…

【UE数字孪生学习笔记】 虚幻日志系统

声明&#xff1a;部分内容来自于b站&#xff0c;知乎&#xff0c;慕课&#xff0c;公开课等的课件&#xff0c;仅供学习使用。如有问题&#xff0c;请联系删除。 部分内容来自UE官方文档&#xff0c;博客等 虚幻日志系统 1. 日志是一种非常实用的调试工具&#xff0c;可以详细…

国内首家!悦数图数据库全项完成中国信通院图数据库性能测试

大数据时代&#xff0c;随着各种社交网络、系统推荐等业务需求的不断发展&#xff0c;数据间的依赖和复杂度的逐渐增加&#xff0c;传统关系型数据库对这些需求捉襟见肘&#xff0c;图数据库应运而生。图数据库在金融风控、知识图谱、关系分析等应用场景的关联查询上有着明显优…

自带红外码库可使用蓝牙小程序控制的离线语音万能红外遥控器

离线语音蓝牙红外模块简介 此蓝牙红外模块是一种低成本的离线语音单麦应用方案&#xff0c;主芯片是一颗专用于语音处理的人工智能芯片&#xff0c;可广泛应用于家电&#xff0c;家居&#xff0c;音箱&#xff0c;玩具&#xff0c;穿戴设备&#xff0c;汽车等产品领域&#xf…

餐饮行业可燃气体报警器计量校准,惠州博罗引领安全新趋势

在惠州博罗这片繁荣的土地上&#xff0c;餐饮行业作为城市经济的重要组成部分&#xff0c;其安全问题一直备受关注。 可燃气体报警器作为餐饮场所预防火灾和爆炸事故的关键设备&#xff0c;其准确性和可靠性至关重要。 在这篇文章中&#xff0c;佰德将通过实际案例和数据&…

基本元器件 - 光电耦合器

光耦是将发光二极管&#xff08;LED&#xff09;和光电探测器集成于一个封装中的器件。 光耦的作用 在光耦中&#xff0c;一次侧&#xff08;LED 侧&#xff09;和二次侧&#xff08;受光器件侧&#xff09;是电绝缘的。因此&#xff0c;即使一次侧和二次侧的电位&#xff08;…

epy - 终端电子书阅读器(epub2、epub3、fb2、mobi)

文章目录 一、关于 epy二、安装epy manual 三、用法四、颜色配置文件五、使用Epy的阅读技巧六、配置文件七、网址支持八、使用鼠标九、文字转语音十、字典十一、Double Spread 一、关于 epy 终端 电子书&#xff08;epub2、epub3、fb2、mobi&#xff09;阅读器 github : http…

力扣每日一题 6/12 + 随机一题

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 2806.取整够买后的账户余额【简单】 题目&#xff1a; 一开始&#xff0c;你…

算法课程笔记——蓝桥第17次直播云课

算法课程笔记——蓝桥第17次直播云课 递归 改成signed&#xff0c;把所有int 改成longlong 100会越界