Websocket通信实战项目(图片互传应用)+PyQt界面+python异步编程(async) (上)服务器端python实现

Rqtz : 个人主页
​​   共享IT之美,共创机器未来    
  Sharing the Beauty of IT and Creating the Future of Machines Together


目录

项目背景

​编辑​专有名词介绍

服务器GUI展示      

功能(位置见上图序号)

客户端GUI展示(h5+css+js),平板,手机

动图​编辑

视频 图片互传-CSDN直播

一键自动获取IP地址

websocket通信实现

按钮映射到新线程启动websocket服务器

 python中使用async异步实现全双工通信,B/S主动发送数据,被动接收数据

 图片二进制转换显示到Qt中label控件(涉及到opencv)

上一张,下一张功能实现

整体代码结构

最后


项目背景

        由于比赛需要,电脑的window系统无法满足要求,因此就需要安装linux系统,采用双系统安装。安装完成之后,发现在手机端与电脑端,电脑端和电脑端进行通信(传输图片)时,没有window上方便,用传统的方式传输的话,大家可能都倾向于QQ或微信,但是在linux上可能就不是那么方便。因此,基于这个问题,我想要开发一个可以在任何平台都可以运行的图片互传软件,一开始想借助python在各个操作系统上的通用,使用CS架构服务器和客户端都编写成基于Pyqt的Gui界面,但是他们虽然支持在电脑端的各个操作系统之间运行,但是安卓或IOS平台就不是很优雅,手机不能使用的话,那就失去了方便性。因此,我将服务器端还保持原有的Pyqt的开发方式,在客户端采用Websocket的方式,原因是websocket支持javascripts编写,这样就可以通过浏览器来建立客户端,而浏览器在任何平台都可以使用,包括安卓和IOS平台,这样的BS架构就非常优雅的解决了安卓平台的限制性。同样这个项目也是对自己学习的一个检验。

经过测试,可以在手机端与电脑端,平板端与电脑端,电脑端与电脑端进行全双工的实时通信

客户端实现请看 Websocket通信实战项目(js)(图片互传应用)(下)客户端H5+css+js实现-CSDN博客

直接看python异步通信async,点击目录

1f9cfc23cdd448dd84f6561211c42dde.png​专有名词介绍

  • websocket协议:

        WebSocket是一种实现在单个TCP连接上进行全双工通信的网络传输协议。这种协议被设计用于改善客户端和服务器之间实时通信的效率,允许双方同时发送和接收信息,而无需像传统HTTP请求那样轮询。

9f79e5f54cb240108201cb63fc0d0f53.png

  • CS架构:

        CS架构则是由客户端和服务器端组成的两层结构,客户端包含业务逻辑和界面展示,服务器端则负责数据管理。这种架构适用于局域网环境,能够提供快速响应和强大的事务处理能力。CS软件通常需要专门安装和维护客户端程序,因此安全性较高,个性化能力较强。然而,这也导致升级和维护成本较高,且兼容性受限于特定操作系统。

  • BS架构

        BS架构是基于浏览器和服务器的体系结构,用户界面通过Web浏览器实现,主要业务逻辑在服务器端处理。这种架构使得软件能够在不同平台上运行,客户端零维护,但个性化能力较低,响应速度相对较慢。由于不需要专门安装客户端程序,只需一个网络链接即可访问,这极大地方便了用户。然而,BS架构对网络稳定性要求较高,对硬件的直接支持较弱。    

    

服务器GUI展示      

b395e41736bb408cb39ec3f4f928af8c.png

功能(位置见上图序号)

  1. 点击按钮启动websocket服务器
  2. 一键自动识别本机 ip地址
  3. 图片接收并显示在窗口中
  4. 图片数量两张及以上时,可使用上一战,下一张切换图片
  5. 支持滚动条,按钮放大缩小图片
  6. 保存客户端发送的图片,支持自定义保存图片路径及名称
  7. 在服务器端主动向客户端发送选择的图片,并显示图片路径
  8. 必要信息输出在窗口中,方便观察。

客户端GUI展示(h5+css+js),平板,手机

80cd66f45e284398a4f07d0485288dbc.png

动图

视频 图片互传-CSDN直播

一键自动获取IP地址

        所谓的自动获取ip地址,本质上是在终端中输入查询ip地址的命令,windows上使用ipconfig,linux(这里是ubuntu)和mac上使用ifconfig,但是使用python要自动获取,省去了打开终端输入命令寻找ip的步骤,就需要使用python的os库,下面请看代码

def autoip(self):if os.name == 'nt':print("当前操作系统是Windows")output = os.popen("ipconfig | findstr \"IPv4\"").read()ip = output.split("\n")self.myapp.ip.setText(ip[1].split(": ")[1]) elif os.name == 'posix':print("当前操作系统是Linux")output = os.popen("ifconfig | awk '/inet /{print $2}'").read()ip = output.split("\n")self.myapp.ip.setText(ip[1]) elif os.name == 'darwin':print("当前操作系统是Mac")output = os.popen("ifconfig en0 | awk '/inet /{print $2}'").read()self.myapp.ip.setText(output) 

1.判断是哪种操作系统

    通过os.name输出的字符串来判断是哪种操作系统:

  • ‘nt’ --> Windows系统
  • ‘posix’ --> Linux系统
  • ‘darwin’ --> Mac系统

2.使用os.popen函数获取命令输出

  • windows系统

output = os.popen("ipconfig | findstr \"IPv4\"").read()

解释:

ipconfig:windows查询ip地址的命令

“I” :将命令通过管道传入 findstr命令(windows特有命令)

findstr \"IPv4\"" :查询命令输出中含有IPV4的那一行,注意\"IPv4\"有双引号

read() : 获取输出

7276123b9e1e476b9a1cbdec8a26489b.png

  • Linux系统

output = os.popen("ifconfig | awk '/inet /{print $2}'").read()

解释:

fconfig:linux查询ip地址的命令

“I” :将命令通过管道传入 awk命令(linux特有命令)

awk '/inet /{print $2}' 查询命令输出中含有IPV4的那一行的第二段字符串

read() : 获取输出

打印之后有两个ip,一个是本地,一个是WLAN,

使用split 函数

ip = output.split("\n")
self.myapp.ip.setText(ip[1])

就可以将ip地址设置到qt的linedit控件中

e23d4b48db464ba9a212133c30e18688.png

样例:

45d0c7e83a664946926382349b082845.png

  • Mac系统

 output = os.popen("ifconfig en0 | awk '/inet /{print $2}'").read()

查询指定装置en0,其他和上述一样

websocket通信实现

按钮映射到新线程启动websocket服务器

1.将按钮通过信号和曹连接到启动新线程函数中

#初始化信号和槽
self.myapp.start.clicked.connect(self.newprocess)

newprocess为启动新的线程的函数

2.启动子线程函数newprocess实现

#启用子线程def newprocess(self):if self.myapp.port.text() == "" or self.myapp.ip.text() == "":self.myapp.picdata.append("【"+str(time.time())+"】"+"【错误】:"+"请输入端口或者ip地址")else:th = threading.Thread(target=self.connect_server)th.start()self.myapp.start.setDisabled(True)

解释:

(1)  if self.myapp.port.text() == "" or self.myapp.ip.text() == "":

判断端口输入框和ip地址输入框是否为空,为空则发出警告,

(2) th = threading.Thread(target=self.connect_server)      th.start()

不为空则可以用使用threading函数来创建一个线程启动websocket服务器

(3)self.myapp.start.setDisabled(True)

       启动成功则可以将按钮设置为不能点击,防止重复启动服务器

问题

        为什么要用一个新的线程呢?因为websocket服务器启动时,会阻塞当前线程,当前有一个主线程用于GUI界面的交互(鼠标点击按钮,拖动页面等),如果服务器在主线程启动,且一直没有客户端连接的话,界面就会卡死,所有按钮都无法点击,因为主线程阻塞。所以要用一个新的线程启动服务器

2.子线程函数connect_server实现,异步,协程

#初始化websocket服务器,异步def connect_server(self):self.emitdata.emit("【提示】:"+"服务器监听中")loop = asyncio.new_event_loop()asyncio.set_event_loop(loop)self.start_server = websockets.serve(self.handler,self.myapp.ip.text(), 8899,max_size=7000000)loop.run_until_complete(self.start_server)loop.run_forever()
#在显式的stop事件循环后,取消所有任务for task in asyncio.all_tasks(loop):  task.cancel()print(task.cancelled())loop.close()

解释:

(1)

self.emitdata.emit("【提示】:"+"服务器监听中")

        由于connect_server是一个子线程,子线程中无法直接访问主线程,emitdata是自定义的信号,通过在合适的位置发射信号,再连接到特定的函数中.

        但是我的ui元素是定义在类的self属性中的,子线程可以直接通过self直接访问它,但是经过测试发现我们在子线程中直接向QTextEdit中增添数据时,会报错QObject::connect: Cannot queue arguments of type 'QTextCursor',因此最好还是使用信号和槽的方式来进行主子线程通信.

(2)

 loop = asyncio.new_event_loop()  asyncio.set_event_loop(loop)

使用python中asyncio库新建一个event_loop事件循环,并且将新创建的事件循环设置为当前线程的事件循环,事件循环是处理异步操作的核心组件

(3)

 self.start_server = websockets.serve(self.handler,self.myapp.ip.text(), 8899,max_size=7000000)

为什么是异步的呢,因为websocket服务器的回调函数self.handler必须是一个异步函数,

websockets.serve函数的参数

  • 第一个参数:服务器连接成功后调用的函数,必须是异步的
  • 第二个参数:ip地址
  • 第三个参数:端口号
  • 第四个参数:传输的最大字节数
  • 还有其他可选参数,这里只用了四个

(4)

 loop.run_until_complete(self.start_server)  loop.run_forever()

运行传入的协程self.start_server,并让事件循环一直运行下去,self.start_server是一个协程

(5)bug

#在显式的stop事件循环后,取消所有任务for task in asyncio.all_tasks(loop):  task.cancel()print(task.cancelled())loop.close()

由于loop.run_forever()会让事件循环一直运行下去,期间会阻塞线程,直到显式的使用stop()方法,这个stop方法的调用是在异步发送数据函数中通过捕捉closeflag标志位来实现的.然后在但前的事件循环中取消所有任务,但是发现有个报错,我一直都没有解决,下文也有提到        

9dd5a032dd59456c93e87a502233d62d.png​ 值的是task4被取消但是仍在挂起状态,这个task4是一个WebSocketServerProtocol.handler().

 python中使用async异步实现全双工通信,B/S主动发送数据,被动接收数据

websocket回调函数

 #websocket处理函数async def handler(self,websocket,path):#创建两个task,分别为发送和接收sendtask = asyncio.get_event_loop().create_task(self.send(websocket))receivetask = asyncio.get_event_loop().create_task(self.receive(websocket))#异步执行await sendtaskawait receivetask

异步发送数据函数

    #异步发送数据async def send(self,websocket):while True:#点击发送图片按钮后,标志位为真if self.sendflag:#此时的self.curr_bytedata存储的二进制数据为选择的图片await websocket.send(self.curr_bytedata)self.sendflag = Falseself.emitdata.emit("发送成功!")#点击断开连接按钮后if self.closeflag:#关闭websocketawait websocket.close()#显式的停止事件循环loop =  asyncio.get_event_loop()loop.stop()#跳出循环,终止协程break#挂起1s,切换到其他协程await asyncio.sleep(1)

异步接收数据函数

    async def receive(self,websocket):self.emitdata.emit(f"客户端连接成功,连接到{websocket.remote_address}")try:async for message in websocket:self.curr_bytedata = message#字节大小print(len(message))self.show_image()except  websockets.ConnectionClosedError:self.emitdata.emit("客户端意外断开连接,请客户端重连")

解释

(1)websocket回调函数handler

  • 由于websocket服务器的回调函数必须是一个异步函数self.handler,因此该函数必须加上async前缀,才可以将其变成一个协程。当服务器检查到有客户端链接过来时就会调用这个回调函数handler。

            当客户端连接后,self.handler将传入以下两个主要参数

  1. websocket: 这是一个websockets.WebSocketServerProtocol实例,它代表服务器端与客户端之间的WebSocket连接。通过这个对象,您可以发送和接收WebSocket帧。
  2. path:这是一个字符串,表示请求的URL路径。对于WebSocket服务器来说,这个值通常是/,但理论上可以是任何值,取决于如何配置websockets.serve函数。
  •  
    sendtask = asyncio.get_event_loop().create_task(self.send(websocket))
    receivetask = asyncio.get_event_loop().create_task(self.receive(websocket))       
             创建出两个task,并且把他们设置到当前的事件循环中。
  •  
    await sendtask      
    await receivetask       
              await 关键字,后面必须跟上一个可等待的对象,例如task,future等,这里面的send和receive就是task对象,使用await关键字就可以将控制权交给evet_loop事件循环。
  • 注意async def为前缀的函数是一个异步函数,必须把它放到事件循环中才可以运行,如果像以往那样子直接调用函数是不会执行的,而是返回一个coroutine对象。

(2)异步发送数据函数与异步接收数据函数

        在网上找到的资料几乎全部都是在服务器受到客户端消息时才向客户端发消息,但是我这个的话,发送图片的这个操作完全是有用户自主决定的,即用户想什么时候发送就什么时候发送,如果只在服务器收到消息才发的话,那也太没意思了。那即要求发送又要求实时接收,首先循环是必要的,但是通信过程中用户并不是时时刻刻 在发送,也不是时时刻刻在接收,因此大多数时间都是在等待的,因此我们需要使用异步休眠的方式在发送的协程和接收的协程之间不断的切换,在await等待的过程中做别的事情,以提高程序的效率。

  •  
      while True:if self.sendflag: ......await asyncio.sleep(1)
             首先创建了一个死循环,不断的判断用户有没有点击发送按钮,即sendflag有没有变为真,判断结束后, await asyncio.sleep(1),异步休眠一秒,这里休眠的作用是可以暂停该协程一秒,来去切换到其他协程,刚刚说了await可以将控制权交给事件循环,事件循环此时就检查当前还有哪些任务可以执行,发现还有一个receivetask可以执行,因此就利用这一秒钟的时间切换到这个receivetask协程,这也就是为什么服务器连接成功后会在窗口打印“客户端连接成功“, 因为利用了这一秒钟执行了receivetask协程中的self.emitdata.emit(f"客户端连接成功,连接到{websocket.remote_address}")。
  •  try:async for message in websocket:self.curr_bytedata = messageprint(len(message)) #字节大小self.show_image()except  websockets.ConnectionClosedError:self.emitdata.emit("客户端意外断开连接,请客户端重连")              
           接着进行try,async for message in websocket将会从websocket中检查有无数据,  注意:这也是一个异步的对象,也使用的async for,也会将控制权交给事件循环,如果此时客户端没有发数据的话,事件循环就会检查当前还有哪些协程可以执行,于是又切会sendtask协程,其实我认为1s之后还是会切换回去。总的来说,这个for循环是只要有数据发来就执行,没数据就等待,这个等待可以切换到别的协程中。
  •        如果此时客户端发来数据时,会将发来的图片的二进制数据,赋值给一个变量,在经过self.show_image()处理显示。下方会有介绍
  • except 检查报错。
  • 如果用户点击了发送按钮,即self.sendflag 为真,
    if  self.sendflag:#此时的self.curr_bytedata存储的二进制数据为选择的图片await websocket.send(self.curr_bytedata)self.sendflag = Falseself.emitdata.emit("发送成功!")

        我们将当前用户选择的图片的二进制格式的数据发送给客户端,使用send方法,发送完成后,该task就结束了,一般很短时间内就发送完成,取决于网络,然后重新将标志位标为假,等待用户下一次点击。

      (3)  关闭连接和停止事件循环(bug)

 #点击断开连接按钮后if self.closeflag:#关闭websocketawait websocket.close()#显式的停止事件循环loop =  asyncio.get_event_loop()loop.stop()#跳出循环,终止协程break
  •         关闭连接后,关闭websocket连接,此时receivetask中的异步循环由于断开了连接,该任务终止,sendtask在关闭连接之后break跳出了循环,sendtask也终止,显式的stop事件循环,最后在conncect_server函数最后有取消掉了没有关闭的任务,但是显示取消失败,并附带报错,和上文提到的bug是同一个,也就是self.handler无法取消,task.canceled()返回false,9dd5a032dd59456c93e87a502233d62d.png我也不知道为什么.希望能看出问题的大佬解答!

 图片二进制转换显示到Qt中label控件(涉及到opencv)

       show_image()显示图像函数实现

 #显示图像def show_image(self):。binarydata = np.frombuffer(self.curr_bytedata,np.uint8)self.image = cv2.imdecode(binarydata,cv2.IMREAD_COLOR)value = cv2.cvtColor(self.image,cv2.COLOR_BGR2RGB)height, width, channels = self.image.shapeimages = QImage(value.data, width, height, width * channels, QImage.Format_RGB888)
#显示图片 self.myapp.image.setPixmap(QPixmap.fromImage(images).scaled(int(width/self.scale_percent),int(height/self.scale_percent)))
解释
  1. binarydata = np.frombuffer(self.curr_bytedata,np.uint8)

    将二进制数据self.curr_bytedata转换为NumPy数组,数据类型为np.uint8。

  2.  self.image = cv2.imdecode(binarydata,cv2.IMREAD_COLOR)

    将二进制数据解码为图self.image,解码格式为彩色(cv2.IMREAD_COLOR)。

  3.  value = cv2.cvtColor(self.image,cv2.COLOR_BGR2RGB)

    将图像从BGR格式转换为RGB格式

  4. height, width, channels = self.image.shape

    获取图像的高度、宽度和通道数

  5. images = QImage(value.data, width, height, width * channels, QImage.Format_RGB888)

    转换成QImage在 ui上显示

  6.  self.myapp.image.setPixmap(QPixmap.fromImage(images).scaled(int(width/self.scale_percent),int(height/self.scale_percent)))

    显示图片,在label控件上

上一张,下一张功能实现

排除重复图片

根据下面代码,在show_image中添加

# 转换成QImage在 ui上显示
#images = QImage(value.data, width, height, width * channels, QImage.Format_RGB888)#中间插入下面的flag = False#将每次显示的不同的图像加入imagelist列表中,为按钮切换上,下张准备for k in range(len(self.imagelist)):if self.curr_bytedata == self.imagelist[k]:flag = Trueif flag == False:self.imagelist.append(self.curr_bytedata)self.number = len(self.imagelist)#图片为2张及以上时使能上一张下一张按钮if self.number > 1:self.myapp.up.setDisabled(False)self.myapp.down.setDisabled(False)#中间插入上面的
#显示图片 
#self.myapp.image.setPixmap(QPixmap.fromImage(images).scaled(int(width/self.scale_percent),int(height/self.scale_percent)))

解释

  1.  for k in range(len(self.imagelist)):if self.curr_bytedata == self.imagelist[k]:flag = True
            首先flag初始为假,这个for循环是指在存储图片二进制数据的imagelist列表中遍历当前的图片历表中是否有重复的,有的话flag为真。                                                          
    if flag == False:self.imagelist.append(self.curr_bytedata)self.number = len(self.imagelist)
       当当前的图片数据没有和之前的重复时,就往该列表imagelist中追加新的数据,self.number为这个列表的长度。                                                                          
  2. if self.number > 1:self.myapp.up.setDisabled(False)self.myapp.down.setDisabled(False)
        图片为2张及以上时使能上一张下一张按钮
上,下一张按钮实现

上一张

#上一张def uppic(self):self.number -= 1if self.number  < 1:self.number = len(self.imagelist)self.curr_bytedata = self.imagelist[self.number-1]self.show_image()else:self.curr_bytedata = self.imagelist[self.number-1]self.show_image()

下一张

#下一张def downpic(self):self.number += 1if self.number > len(self.imagelist):self.number = 1self.curr_bytedata = self.imagelist[self.number-1]self.show_image()else:self.curr_bytedata = self.imagelist[self.number-1]self.show_image()          

  解释

        本质上是改变self.number(上面有提到)的值来对应到imagelist图片列表当中的索引,

达到最大值,或最小值时切换到列表的最小值,最大值。

整体代码结构

956387e3955e48988943dc25e64f0278.png

最后

这篇文章是我初次接触websocket和异步async写的一个小项目,可能有理解不到位的地方.

如果上述有误,请各位大佬及时批评指正,小弟感激不尽。

7f1ef757c074450595905c3a5ab92b2f.png213f43fc707047edaabddcc615b9d05a.png

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

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

相关文章

固相提取铕和铀

固相萃取&#xff08;Solid Phase Extraction&#xff0c;SPE&#xff09;是一种常用的化学分离技术&#xff0c;它利用固体吸附剂&#xff08;固定相&#xff09;与样品中的目标化合物&#xff08;流动相&#xff09;之间的相互作用力&#xff0c;将目标化合物从样品中分离出来…

[Redis]哨兵机制

哨兵机制概念 在传统主从复制机制中&#xff0c;会存在一些问题&#xff1a; 1. 主节点发生故障时&#xff0c;进行主备切换的过程是复杂的&#xff0c;需要人工参与&#xff0c;导致故障恢复时间无法保障。 2. 主节点可以将读压力分散出去&#xff0c;但写压力/存储压力是无法…

python 发布应用程序包

文章目录 发布python包toml配置文件构建发布python包 官方文档参考 将自己的python项目发布成源码包或者wheel二进制包,供其他开发者使用。 方式: 使用py工具; distutils,该工具的使用已过时;setuptools,常用方式;wheel,在setuptools的基础上添加了 bdist_wheel, …

图形的搭建

例一&#xff1a; 输入描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示输出的行数&#xff0c;也表示组成“X”的反斜线和正斜线的长度。 输出描述&#xff1a; 针对每行输入&#xff0c;输出用“*”组成的X形图案。 示例一&…

智能合约与企业数字化转型:案例分析与未来展望

随着区块链技术的快速发展&#xff0c;智能合约作为其重要应用之一&#xff0c;正逐渐成为推动企业数字化转型的关键工具。智能合约不仅可以自动执行和验证合同&#xff0c;还能够增强数据安全性、优化业务流程&#xff0c;并提升企业间的信任和透明度。本文将深入探讨智能合约…

法国工程师IMT联盟 密码学及其应用 2023年期末考试补考题

1 JAVA 安全 1.1 问题1 1.1.1 问题 用 2 或 3 句话解释 Java 执行模型&#xff08;Java 虚拟机machine virtuelle Java)&#xff09;中引入introduit沙箱bac sable机制 mcanisme d’excution par isolation的目的。 1.1.2 问题解释 在 Java 执行模型&#xff08;Java 虚拟机…

【12321骚扰电话举报受理中心-短信验证安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

​埃文科技受邀出席2024 “数据要素×”生态大会​

2024“数据要素”生态大会&#xff08;以下简称“大会”&#xff09;于2024年6月30日在河南省郑州市举办&#xff0c;大会主题为“加快数据要素化进程 推动新质生产力发展”。 本次大会旨在搭建高水平交流合作平台、分享前沿观点、展示先进技术、交流实践经验&#xff0c;共同探…

开源模型应用落地-FastAPI-助力模型交互-WebSocket篇(五)

一、前言 使用 FastAPI 可以帮助我们更简单高效地部署 AI 交互业务。FastAPI 提供了快速构建 API 的能力,开发者可以轻松地定义模型需要的输入和输出格式,并编写好相应的业务逻辑。 FastAPI 的异步高性能架构,可以有效支持大量并发的预测请求,为用户提供流畅的交互体验。此外,F…

一个pdf分成,一个pdf分成两个,单个pdf分成多个

在数字化时代&#xff0c;pdf文件因其跨平台兼容性强、格式稳定等特点而成为工作与学习中的常用文件格式。然而&#xff0c;有时候我们可能只需要pdf文件中的某几个页面&#xff0c;而不是整个文档。这时&#xff0c;将一个pdf分成多个文件就变得尤为重要。本文将为你介绍几种简…

谷粒商城学习笔记-05-项目微服务划分图

文章目录 一&#xff0c;商城业务服务-前端服务二&#xff0c;商城业务服务-后端服务三&#xff0c;存储服务四&#xff0c;第三方服务五&#xff0c;服务治理六&#xff0c;日志七&#xff0c;监控预警系统1&#xff0c;Prometheus2&#xff0c;Grafana3&#xff0c;Prometheu…

SQLite

1、SQLite简介 轻量化、易用的嵌入式数据库&#xff0c;用于设备端的数据管理&#xff0c;可以理解成单点的数据库。传统服务器型数据库用于管理多端设备&#xff0c;更加复杂。 SQLite是一个无服务器的数据库&#xff0c;是自包含的。这也称为嵌入式数据库&#xff0c;这意味着…

数据分析三剑客-Matplotlib

数据分析三剑客 数据分析三剑客通常指的是在Python数据分析领域中&#xff0c;三个非常重要的工具和库&#xff1a;Pandas、NumPy和Matplotlib。Pandas主要负责数据处理和分析&#xff0c;NumPy专注于数值计算和数学运算&#xff0c;而Matplotlib则负责数据可视化。这三个库相…

【安全攻防】网络安全中的序列化与反序列

1.序列化与反序列化 首先要了解序列化与反序列化的定义&#xff0c;以及序列化反序列化所用到的基本函数。 序列化&#xff1a;把对象转换为字节序列的过程称为对象的序列化&#xff0c;相当于游戏中的存档。 PHP中的序列化函数serialize() **serialize()**函数用于序列化对…

Appium自动化测试框架3

滑动与拖拽 swipe 滑动时间的长短会影响最后的结果的 是有一定误差的 from appium import webdriver import time # 启动一个字典 包装相应的启动参数 desired_caps dict() # 平台的名字&#xff0c;安卓还是IOS 大小写无所谓 desired_caps[platformName] Android # 平台的…

【串口通信】之TTL电平

1. 什么是串口 串口,全称为串行通信端口,是一种计算机硬件接口,用于实现数据的串行传输。与并行通信不同,串口通信一次只传输一个比特,数据通过串行线按顺序传输。串口通信在嵌入式系统、工业控制、计算机与外围设备通信等领域非常常见 2. 什么是串口通信 串口通信是指通过…

Docker 部署 Minio 对象存储服务器

文章目录 Github官网文档简介dockerdocker-compose.ymlmc 客户端mc 基础命令Golang 示例创建 test 账号密钥文件上传示例 Github https://github.com/minio/minio 官网 https://min.io/https://www.minio.org.cn/ 文档 https://www.minio.org.cn/docs/minio/kubernetes/up…

刚办理的手机号被停用,你可能遇到这些问题了!

很多朋友都会遇到手机号被停用的情况&#xff0c;那么你知道你的手机号为什么会被停用吗&#xff1f;接下来&#xff0c;关于手机号被停用的问题&#xff0c;跟着小编一块来了解一下吧。 ​停机的两种形态&#xff1a; 1、第一个是局方停机&#xff0c;即语音、短信和流量都不…

C++|哈希应用->布隆过滤器

目录 一、概念 二、模拟实现 三、布隆过滤器扩展应用 上一篇章学习了位图的使用&#xff0c;但它只适用于整数&#xff0c;对于要查询字符串是否在不在&#xff0c;位图并不能解决。所以针对这一问题&#xff0c;布隆过滤器可以派上用场&#xff0c;至于布隆过滤器是什么&am…

Java支付宝沙箱支付环境配置及简单测试

Java支付宝沙箱环境配置(测试) 1. 沙箱配置环境 沙箱应用 - 开放平台 (alipay.com) 2. 需要用到的基本信息 3. Pom文件添加依赖 <!--支付宝依赖 --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-easysdk</artifactId…