当前位置: 首页 > news >正文

[Python学习日记-79] socket 开发中的粘包现象(解决模拟 SSH 远程执行命令代码中的粘包问题)

[Python学习日记-79] socket 开发中的粘包现象(解决模拟 SSH 远程执行命令代码中的粘包问题)

简介

粘包问题底层原理分析

粘包问题的解决

简介

        在Python学习日记-78我们留下了两个问题,一个是服务器端 send() 中使用加号的问题,另一个是收的 recv() 中接收长度导致的粘包现象。

        上图就是粘包现象,就是指两次结果粘到一起了,它的发生主要是因为 socket 缓冲区导致的,粘包对于用户体验造成的影响是比较大,难度也相对较高,所以本篇的主角就是粘包现象,我们一起来看看有什么办法可以解决这个难搞的现象。

粘包问题底层原理分析

         在了解什么是粘包之前我们必须知道一个前提,那就是粘包现象只会出现在 TCP 身上,而 UDP 是永远不会粘包的,要知道是什么原因我们要先掌握一个 socket 收发消息的原理先,下图为 sokcet 收发消息的原理图

         在发送端和接收端之间怎么样为一条消息呢?可以认为一次 send() 和 recv() 就是一条消息,但要知道你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用户态复制到内核态,这样的操作是耗资源和时间的,频繁的在内核态和用户态之前交换数据势必会导致发送效率降低,因此 socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方(send() 的字节流是先放入应用程序所在计算机的缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用 sendall() 就会循环调用 send(),数据不会丢失),所以这条消息无论底层是如何分段分片的传输层协议都会把构成整条消息的数据段排序完成后才呈现在内核缓冲区,所以到达了缓冲区其实都是一条完整的消息,关键就在与传输协议 TCP 和 UDP 的传输方式不一样,导致两者的特性各不相同。

        TCP 协议(流式协议)传输消息时发送端可能会一次性发送 1KB 的数据,而接收端可能会以 2KB、3KB、6KB、3Bytes 的形式来提取收到的数据,也就是说接收端所看到的数据是一个流(stream),即面向流的通信是无消息保护边界的协议,所以客户端是不能一下子看到一条消息是有多少字节的,例如基于 TCP 的套接字客户端往服务器端上传文件,发送时文件内容是按照一段一段的字节流发送的,在服务器端接收到后根本不知道该文件的字节流从何处开始,在何处结束。TCP 为提高传输效率,发送方往往要收集到足够多的数据后才发送一个 TCP 段,如果连续几次需要发送的数据都很少,通常 TCP 会根据优化算法(Nagle 算法)把这些数据合成一个 TCP 段后一次发送出去,当发送端缓冲区的长度大于网卡的 MTU 时会出现拆包情况的发生,届时 TCP 会将这次发送的数据拆成几个数据包发送出去,这样更加加重了 TCP 传输数据的粘包问题,这就是 TCP 为什么容易发生粘包问题的原因。但 TCP 的数据不会丢,在上一次传输没有收完的包,下次还会接收,发送端会在收到 ack 时才会清除缓冲区内容,所以数据是可靠传输的,缺点就是会粘包。

        UDP 协议传输消息是必须以消息为单位提取数据的,不能一次提取任意字节的数据,即面向消息的通信是有消息保护边界的,它也不会使用块的合并优化算法来进行优化,并且由于 UDP 支持的是一对多的模式,所以接收端的 skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的 UDP 包,在每个 UDP 包中就有了消息头(消息来源地址,端口等信息),对于接收端来说就容易进行区分处理了,所以 UDP 协议传输消息永远不可能出现粘包现象。但 UDP 的 recvfrom() 是阻塞的,一个 recvfrom(x) 必须对唯一一个 sendinto(y),收完了 x 个字节的数据就算完成,若是 y>x 那么 y-x 的数据就会丢失,这意味着 UDP 根本不会粘包,但是会丢数据,并不可靠。

        总的来说,所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

以下两种情况会发生粘包:

1、发送端需要等缓冲区满才发送出去,从而造成粘包(发送数据时间间隔很短,而且数据量很小,会合到一起产生粘包)

服务器端:

import socketip_port = ('127.0.0.1',8080)server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(ip_port)
server.listen(5)conn,client_addr = server.accept()data1 = conn.recv(10)
data2 = conn.recv(10)print('第一次------>', data1.decode('utf-8'))
print('第二次------>', data2.decode('utf-8'))conn.close()

客户端:

import socketip_port = ('127.0.0.1',8080)
info_size = 1024client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
client.connect(ip_port)client.send('hello'.encode('utf-8'))
client.send('jove'.encode('utf-8'))

代码输出如下:

2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

服务器端:

import socket
import time
ip_port = ('127.0.0.1',8080)server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(ip_port)
server.listen(5)conn,client_addr = server.accept()data1 = conn.recv(2)    # 第一次没接收完整
data2 = conn.recv(10)   # 第二次接收的时候会先取出旧的数据,然后再取新的print('第一次------>', data1.decode('utf-8'))
time.sleep(1)
print('第二次------>', data2.decode('utf-8'))conn.close()

客户端:

import socketip_port = ('127.0.0.1',8080)
info_size = 1024client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
client.connect(ip_port)client.send('hello'.encode('utf-8'))
client.send('jove'.encode('utf-8'))

代码输出如下: 

粘包问题的解决

一、struct 模块

        解决粘包问题的关键就是要何如提前告诉接收端我发送的信息长度,我们的解决办法就是为真正的数据封装一个固定长度的报头,然后让接收端按照固定长度来接受该报头从而获取到我接受数据的长度大小,而 struct 模块就是用于数据的打包和解包。

        通过 struct 模块,可以将 Python 中的数据类型(如整数、浮点数等)转换为指定的二进制格式,或者将二进制数据解包成相应的 Python 对象。该模块提供了一些函数来执行这些转换,包括 pack()、unpack()、pack_into()、unpack_from() 等。其中,pack() 函数用于将数据打包为二进制字符串,unpack() 函数用于将二进制数据解包为 Python 对象。struct 模块定义了一些格式字符用于表示数据的布局、对齐方式和字节顺序。常用的格式字符包括:'i'(有符号整数)、'l'(有符号长整数)、'q'(有符号的长长整数)、'f'(浮点数)、's'(字符串)、'c'(单个字符)等。

代码演示:

import struct# 发送端打包,可以一次打包两个不同类型的数据,一个数据长度为4,两个数据长度为8,如此类推
res = struct.pack('if',12888,3.14)  # 'i' == int 'f' == float
print(res,type(res),len(res))# 接收端固定长度接收,client.recv(4)
obj = struct.unpack('if',res)
print(obj)    # 解包后是一个元组
print(obj[0])# res = struct.pack('i',12888888888)  # 'i'会超过范围报错

代码输出如下:

二、简单版本

服务器端:

import socket
import subprocess
import structip_port = ('127.0.0.1',8080)
cmd_size = 8096server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(ip_port)
server.listen(5)print('starting...')
while True:  # 链接循环conn, client_addr = server.accept()print(client_addr)while True:  # 通讯循环try:# 1、收命令cmd = conn.recv(cmd_size)   # 8096个字节的命令已经很好的保证了命令可以完整接收if not cmd: break# 2、执行命令,拿到结果obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)stdout = obj.stdout.read()stderr = obj.stderr.read()# 3、把命令的结果返回给客户端# 第一步: 制作固定长度的报头total_size = len(stdout) + len(stderr)header = struct.pack('i', total_size)# 第二步: 把报头(固定长度)发送给客户端conn.send(header)# 第三步: 再发送真实的数据conn.send(stdout)  # 这里不使用 +(加号) TCP/IP也会把两个包粘到一起conn.send(stderr)except ConnectionResetError:breakconn.close()
server.close()

客户端:

import socket
import structip_port = ('127.0.0.1',8080)
info_size = 1024client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
client.connect(ip_port)while True:# 1、发命令cmd = input('>>: ').strip()if not cmd:continueclient.send(cmd.encode('utf-8'))# 2、拿到执行命令的结果,并打印# 第一步: 先收报头header = client.recv(4)# 第二步: 从报头中解析出对真实数据的描述信息(数据的长度)total_size = struct.unpack('i', header)[0]# 第三步: 接收真实的数据recv_size = 0recv_data = b''while recv_size < total_size:res = client.recv(info_size)recv_data += resrecv_size += len(res)  # 计算真实的接收长度,如果以后增加打印进度条的时候就可以精确无误的表示print(recv_data.decode('gbk'))client.close()

代码输出如下:

        很明显已经没有粘包现象了,虽然解决了粘包的问题,但是还是存在包头信息过少的问题,例如我想客户端接收到数据后验证一下数据的完整性,那目前就无法完成这一功能了,并且打包的数据长度还会受到数据格式的限制,而在终极版当中这一切将会得到解决。

三、终极版本

服务器端:

import socket
import subprocess
import struct
import jsonip_port = ('127.0.0.1',8080)
cmd_size = 8096server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(ip_port)
server.listen(5)print('starting...')
while True:  # 链接循环conn, client_addr = server.accept()print(client_addr)while True:  # 通讯循环try:# 1、收命令cmd = conn.recv(cmd_size)   # 8096个字节的命令已经很好的保证了命令可以完整接收if not cmd: break# 2、执行命令,拿到结果obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)stdout = obj.stdout.read()stderr = obj.stderr.read()# 3、把命令的结果返回给客户端# 第一步: 制作报头header_dic = {  # 使用字典,解决了报头信息少的问题'filename': 'a.txt','md5': 'xxxxdxxx','total_size': len(stdout) + len(stderr)}header_json = json.dumps(header_dic)header_bytes = header_json.encode('utf-8')# 第二步: 先发送报头长度conn.send(struct.pack('i',len(header_bytes)))  # 字典的bytes的长度很小,'i'已经足够使用了# 第三步: 再发报头conn.send(header_bytes)# 第四步: 再发送真实的数据conn.send(stdout)  # 这里不使用+ TCP/IP也会把两个包粘到一起conn.send(stderr)except ConnectionResetError:breakconn.close()
server.close()

客户端:

import socket
import struct
import jsonip_port = ('127.0.0.1',8080)
info_size = 1024client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
client.connect(ip_port)while True:# 1、发命令cmd = input('>>: ').strip()if not cmd:continueclient.send(cmd.encode('utf-8'))# 2、拿到执行命令的结果,并打印# 第一步: 先收报头的长度obj = client.recv(4)header_size = struct.unpack('i',obj)[0]# 第二步: 再收报头header_bytes = client.recv(header_size)# 第三步: 从报头中解析出对真实数据的描述信息header_json = header_bytes.decode('utf-8')header_dic = json.loads(header_json)total_size = header_dic['total_size']# 第四步: 接收真实的数据recv_size = 0recv_data = b''while recv_size < total_size:res = client.recv(info_size)recv_data += resrecv_size += len(res)  # 计算真实的接收长度,如果以后增加打印进度条的时候就可以精确无误的表示print(recv_data.decode('gbk'))client.close()

代码输出如下:

        终极版当中报头使用了字典的形式,并且用 json 模块进行格式化,然后再用 struct 模块进行打包,这样报头就能包含更多的数据,从而实现更多的功能了,并且打包时不会再受到数据格式的限制。

相关文章:

[Python学习日记-79] socket 开发中的粘包现象(解决模拟 SSH 远程执行命令代码中的粘包问题)

[Python学习日记-79] socket 开发中的粘包现象&#xff08;解决模拟 SSH 远程执行命令代码中的粘包问题&#xff09; 简介 粘包问题底层原理分析 粘包问题的解决 简介 在Python学习日记-78我们留下了两个问题&#xff0c;一个是服务器端 send() 中使用加号的问题&#xff0c…...

【数据结构】深入解析:构建父子节点树形数据结构并返回前端

树形数据结构列表 一、前言二、测试数据生成三、树形代码3.1、获取根节点3.2、遍历根节点&#xff0c;递归获取所有子节点3.3、排序3.4、完整代码 一、前言 返回前端VO对象中&#xff0c;有列情况列表展示需要带树形结构&#xff0c;例如基于RBAC权限模型中的菜单返回&#xf…...

【统计的思想】假设检验(二)

假设检验是根据人为设定的显著水平&#xff0c;对被测对象的总体质量特性进行统计推断的方法。 如果我们通过假设检验否定了零假设&#xff0c;只是说明在设定的显著水平下&#xff0c;零假设成立的概率比较小&#xff0c;并不是说零假设就肯定不成立。如果零假设事实上是成立…...

IT服务规划设计

1. IT服务设计的作用 1) 设计满足需求的IT服务。 2) 设计SAL,测量方法和指标。 3) 设计服务过程及控制方法。...

高效查找:二分查找算法解析

1.二分查找简介 二分查找算法&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;适用于有序数组或序列。它的基本思想是通过逐步缩小查找范围&#xff0c;将查找区间一分为二&#xff0c;直到找到目标值或确定目标值不存在。 算法原理&#xff1a;在数组…...

电脑办公技巧之如何在 Word 文档中添加文字或图片水印

Microsoft Word是全球最广泛使用的文字处理软件之一&#xff0c;它为用户提供了丰富的编辑功能来美化和保护文档。其中&#xff0c;“水印”是一种特别有用的功能&#xff0c;它可以用于标识文档状态&#xff08;如“草稿”或“机密”&#xff09;、公司标志或是版权信息等。本…...

我的求职之路合集

我把我秋招和春招的一些笔面试经验在这里发一下&#xff0c;网友们也可以参考一下。 我的求职之路&#xff1a;&#xff08;1&#xff09;如何谈自己的缺点 我的求职之路&#xff1a;&#xff08;2&#xff09;找工作时看重的点 我的求职之路&#xff1a;&#xff08;3&…...

FPGA自分频产生的时钟如何使用?

对于频率比较小的时钟&#xff0c;使用clocking wizard IP往往不能产生&#xff0c;此时就需要我们使用代码进行自分频&#xff0c;自分频产生的时钟首先应该经过BUFG处理&#xff0c;然后还需要进行时钟约束&#xff0c;处理之后才能使用。...

【2025最新计算机毕业设计】基于SpringBoot+Vue爬虫技术的咖啡与茶饮料文化平台(高质量源码,可定制,提供文档,免费部署到本地)

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…...

jmeter中对接口进行循环请求后获取相应数据

1、工作中遇到一个场景就是对某个单一接口进行循环请求&#xff0c;并需要获取每次请求后返回的相应数据&#xff1b; 2、首先就在jmeter对接口相关组件进行配置&#xff0c;需要组件有&#xff1a;循环控制器、CSV数据文件设置、计数器、访问接口、HTTP信息头管理器、正则表达…...

RocketMQ 怎么保证消息的可靠性?

目录 1. 消息发送可靠性 1.1 同步发送 1.2 异步发送 1.3 发送重试 1.4 事务消息 2. 消息存储可靠性 2.1 CommitLog 持久化 2.2 刷盘机制 2.3 主从复制 2.4 消息索引 3. 消息消费可靠性 3.1 消费确认机制 3.2 消费重试机制 3.3 消费位点管理 3.4 集群消费与广播消…...

基于 Node.js 的天气查询系统实现(附源码)

项目概述 这是一个基于 Node.js 的全栈应用,前端使用原生 JavaScript 和 CSS,后端使用 Express 框架,通过调用第三方天气 API 实现天气数据的获取和展示。 主要功能 默认显示多个主要城市的天气信息 支持城市天气搜索 响应式布局设计 深色主题界面 优雅的加载动画 技术栈 …...

C++函数初识

文章目录 一、形参带默认值的函数二、inline内联函数三、函数重载 一、形参带默认值的函数 给默认值的时候&#xff0c;从右向左给&#xff1b;调用效率的问题&#xff1b;定义处可以给形参默认值&#xff0c;声明也可以给形参默认值&#xff1b;形参给默认值的时候&#xff0…...

代码随想录day3

203:移除链表元素&#xff1a;注意虚拟头节点的使用 ListNode* removeElements(ListNode* head, int val) {ListNode* result new ListNode();result->next head;ListNode* current result;while(current ! nullptr && current->next ! nullptr){if(current-…...

论文+AI赋能教育:探索变革路径与创新实践。包括word和pdf格式。

下载地址链接&#xff1a; https://download.csdn.net/download/wanggang130532/90292129https://download.csdn.net/download/wanggang130532/90292129...

ray.rllib 入门实践-5: 训练算法

前面的博客介绍了ray.rllib中算法的配置和构建&#xff0c;也包含了算法训练的代码。 但是rllib中实现算法训练的方式不止一种&#xff0c;本博客对此进行介绍。很多教程使用 PPOTrainer 进行训练&#xff0c;但是 PPOTrainer 在最近的 ray 版本中已经取消了。 方式1&#xff1…...

uniapp 在线更新应用

在线更新应用及进度条显示 1.比较现安装手机中的apk 与线上apk的版本 getVersion(){var newVersionuni.getStorageSync("newVersion").split(".")var versionplus.runtime.version.split(".") // 获取手机安装的版本var versionNum""…...

中间件安全

一.中间件概述 1.中间件定义 介绍&#xff1a;中间件&#xff08;Middleware&#xff09;作为一种软件组件&#xff0c;在不同系统、应用程序或服务间扮演着数据与消息传递的关键角色。它常处于应用程序和操作系统之间&#xff0c;就像一座桥梁&#xff0c;负责不同应用程序间…...

从根源分析,调试,定位和解决MacOS ld: unsupported tapi file type ‘!tapi-tbd‘ in YAML file

你要是遇到同样错误&#xff0c;找一圈都没有解决&#xff0c;建议认真读一下本文&#xff0c;这个应该是最终极的解决办法&#xff0c;从原理上剖析了产生的原因&#xff0c;同时给出来了调试和定位的办法。 maccos使用brew安装了一个gcc14, 结果编译一个最简单的程序都报错&a…...

Linux的权限和一些shell原理

目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型&#xff1a; 基本权限&#xff1a; chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后&#xff1a; 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…...

构建企业级React应用的进阶实践

构建企业级React应用的进阶实践 在当今前端开发领域&#xff0c;React凭借其组件化架构和声明式编程范式&#xff0c;已成为构建复杂用户界面的首选方案。本文将深入探讨React的高级应用场景&#xff0c;通过一系列精心设计的代码示例&#xff0c;展示如何打造高性能、可维护的…...

2024年度总结:技术探索与个人成长的交织

文章目录 前言年度创作回顾&#xff1a;技术深耕与分享数据库技术&#xff1a;MySQL 与 MyBatisJava 及相关技术栈计算机网络&#xff1a;构建网络知识体系思维方式的转变&#xff1a;构建技术知识体系的桥梁 项目实践&#xff1a;人工智能与智慧医疗的碰撞生活与博客的融合与平…...

mysql-06.JDBC

目录 什么是JDBC: 为啥存在JDBC: JDBC工作原理&#xff1a; JDBC的优势&#xff1a; 下载mysql驱动包&#xff1a; 用java程序操作数据库 1.创建dataSource: 2.与服务端建立连接 3.构造sql语句 4.执行sql 5.关闭连接&#xff0c;释放资源 参考代码&#xff1a; 插…...

arm-linux平台、rk3288 SDL移植

一、所需环境资源 1、arm-linux交叉编译器&#xff0c;这里使用的是gcc-linaro-6.3.1 2、linux交叉编译环境&#xff0c;这里使用的是Ubuntu 20.04 3、sdl2源码 https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.11.tar.gz 二、代码编译 1、解压sdl2源码…...

CentOS 上安装 Go (Golang)

1. 检查系统环境 确保系统为 CentOS 7 或 CentOS 8&#xff0c;或者其他兼容的 Linux 发行版。 cat /etc/os-release2. 安装依赖 安装一些必要的工具&#xff1a; sudo yum update -y sudo yum install -y wget tar3. 下载 Go 从 Go 官方下载页面获取适用于 Linux 的最新版…...

小哆啦解题记:整数转罗马数字

小哆啦解题记&#xff1a;整数转罗马数字 小哆啦开始力扣每日一题的第十四天 https://leetcode.cn/problems/integer-to-roman/submissions/595220508/ 第一章&#xff1a;神秘的任务 一天&#xff0c;哆啦A梦接到了一项任务——将一个整数转换为罗马数字。他心想&#xff1a;…...

碰撞体问题

用点检测2d物体是否有物体 功能要求是点击空白处取消选中&#xff0c;点击棋子选中所以我做了一个射线检测。但是脑子的惯性让我用的是3D的射线检测。但我们这是一个2D游戏啊。 Vector3 mousePos pos;mousePos.z 10f; // 假设你需要转换到距离相机10单位的世界位置Vector3 …...

HTML<label>标签

例子 三个带标签的单选按钮&#xff1a; <form action"/action_page.php"> <input type"radio" id"html" name"fav_language" value"HTML"> <label for"html">HTML</label><br&…...

「 机器人 」利用数据驱动模型替代仿真器:加速策略训练并降低硬件依赖

前言 在强化学习(Reinforcement Learning, RL)中,策略训练需要大量的交互数据(状态、动作、奖励、下一状态),而这些数据通常来自仿真器或真实硬件。传统高保真仿真器虽然能在一定程度上模拟飞行器的动力学,但往往计算量大、开发成本高,且仍可能与真实环境存在差距。为此…...

.Net Core微服务入门全纪录(六)——EventBus-事件总线

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…...

QD Laser携“Lantana”激光器参展SPIE光子学西部展2025,聚焦紧凑型设计

据悉&#xff0c;QD Laser公司将在2025年SPIE光子学西部展览会上展出其最新产品——世界最小一体化紧凑型可见光激光器“Lantana”。该展会将于1月28日至30日在旧金山的Moscone中心举行。 在展会期间&#xff0c;QD Laser公司将现场展示这款超小型、轻便设备—— “Lantana”。…...

Docker + Nginx 部署个人静态博客指南

本文是一个使用 Docker 和 Nginx 部署个人静态博客的指南。通过本指南&#xff0c;您可以快速了解如何使用 Docker 和 Nginx 部署自己的静态博客网站。 前提 在开始使用本指南之前&#xff0c;请具备以下前提&#xff1a; 首先你得有个服务器服务器已经安装好Git、Vim等工具一…...

springboot3 集成 knife4j(接口文档)

提示&#xff1a;文章是集成 knife4j&#xff0c;而非 swagger2 或者 swagger3&#xff0c;效果如图 文章目录 前言一、添加依赖二、如何集成1.配置文件2.注解部分1.Tag2.Operation3.Parameter4.Schema 3.使用 总结 前言 提示&#xff1a;&#xff1a;大家在开发阶段&#xff…...

批量创建ES索引

7.x from elasticsearch import Elasticsearch# 配置 Elasticsearch 连接 # 替换为你的 Elasticsearch 地址、端口、用户名和密码 es Elasticsearch([http://10.10.x.x:43885],basic_auth(admin, XN272G9THEAPYD5N5QORX3PB1TSQELLB) )# # 测试连接 # try: # # 尝试获取集…...

单路由及双路由端口映射指南

远程登录总会遇到登陆不上的情况&#xff0c;可能是访问的大门没有打开哦&#xff0c;下面我们来看看具体是怎么回事&#xff1f; 当软件远程访问时&#xff0c;主机需要两个条件&#xff0c;一是有一个唯一的公网IP地址&#xff08;运营商提供&#xff09;&#xff0c;二是开…...

基于Springboot + vue实现的民俗网

“前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff1a;人工智能学习网站” &#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜…...

動態住宅IP提升網站訪問成功率

動態住宅IP通常與普通家庭用戶的網路連接相關聯。這種IP地址的特點在於&#xff0c;它是動態變化的&#xff0c;用戶在每次連接時可能會獲得不同的IP地址。這與靜態IP形成了鮮明對比&#xff0c;後者在連接期間保持不變。傳統上&#xff0c;IP地址分為住宅IP和數據中心IP兩類。…...

Java集合学习:HashMap的原理

一、HashMap里的Hash是什么&#xff1f; 首先&#xff0c;我们先要搞清楚HashMap里的的Hash是啥意思。 当我们在编程过程中&#xff0c;往往需要对线性表进行查找操作。 在顺序表中查找时&#xff0c;需要从表头开始&#xff0c;依次遍历比较a[i]与key的值是否相等&#xff…...

使用rsync+inotify简单实现文件实时双机双向同步

使用rsyncinotify简单实现文件实时双机双向同步 实现思路 使用inotify-tools的inotifywait工具监控文件变化&#xff0c;触发后使用rsync做同步。加入系统服务项&#xff0c;实现实时监听&#xff0c;方便管理。 以下配置操作&#xff0c;单向同步&#xff0c;只需在单边部…...

[JavaScript] ES6及以后版本的新特性

文章目录 箭头函数&#xff08;Arrow Functions&#xff09;为什么需要箭头函数&#xff1f;箭头函数的完整语法箭头函数中的 this实用场景 解构赋值&#xff08;Destructuring Assignment&#xff09;为什么需要解构赋值&#xff1f;数组解构赋值的完整用法对象解构赋值的完整…...

IO进程 寒假作业

一、请使用消息队列实现2个终端之间互相聊天 #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/wait.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> …...

无公网IP 外网访问媒体服务器 Emby

Emby 是一款多媒体服务器软件&#xff0c;用户可以在 Emby 创建自己的个人多媒体娱乐中心&#xff0c;并且可以跨多个设备访问自己的媒体库。它允许用户管理传输自己的媒体内容&#xff0c;比如电影、电视节目、音乐和照片等。 本文将详细的介绍如何利用 Docker 在本地部署 Emb…...

【2025小年源码免费送】

&#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜&#xff0c; &#x1f525;码农福利等你领&#xff01; &#x1f496;山高路远坑又深&#xff0c; &#x1f4d5;大军纵横任驰奔&#xff0c; &#x1f389;谁敢横刀立马行…...

哈希表示例

示例1 两数之和 "两数之和"&#xff08;Two Sum&#xff09;是LeetCode上的一个经典算法问题&#xff0c;编号为1&#xff0c;它要求在一个整数数组nums中找到两个不同的索引i和j&#xff0c;使得nums[i] nums[j] target。 问题描述&#xff1a; 给定一个整数数…...

VS企业版和专业版的区别

网上查询vs分析dump文件&#xff0c;查找托管内存泄露&#xff0c;需要使用“调试托管内存”功能&#xff0c;当前安装的vs2022 专用版找不到这个选项&#xff0c;vs2015是ok的&#xff0c;比较版本发现2022是专业版&#xff0c;2015是企业版。网上搜索专业版和企业版差异如下&…...

YOLOv10-1.1部分代码阅读笔记-train.py

train.py ultralytics\models\yolov10\train.py 目录 train.py 1.所需的库和模块 2.class YOLOv10DetectionTrainer(DetectionTrainer): 1.所需的库和模块 from ultralytics.models.yolo.detect import DetectionTrainer from .val import YOLOv10DetectionValidator fr…...

autogen 自定义agent (1)

目录 第一个自定义agent&#xff1a;CountDownAgent代码运行逻辑1. 创建 CountDownAgent 代理2. 处理消息3. 运行 CountDownAgent 另一种调用方式类似的agent: CountUpAgent 第一个自定义agent&#xff1a;CountDownAgent from typing import AsyncGenerator, List, Sequence,…...

ssh密钥登录GitHub时一直提示“Error: Permission denied (publickey)”

起因 环境&#xff1a;Windows10 背景&#xff1a;之前就是按照官方说明创建个rsa密钥&#xff0c;在git后台添加上&#xff0c;就行了&#xff0c;近期怎么添加怎么失败&#xff0c;总是“Error: Permission denied (publickey)”的提示&#xff01; 尝试 各种尝试&#xf…...

多模态数据融合的基本流程与关键环节

多模态数据融合作为人工智能的重要技术方向&#xff0c;不仅整合了视觉、语言、语音、传感器等多种模态的数据&#xff0c;还通过合理的融合方法让机器获得更全面的感知能力。那么&#xff0c;多模态数据融合的过程是怎样的&#xff1f;有哪些关键环节需要注意&#xff1f;今天…...

k8s 蓝绿发布、滚动发布、灰度发布

在Kubernetes&#xff08;k8s&#xff09;中&#xff0c;蓝绿发布、滚动发布、灰度发布&#xff08;金丝雀发布&#xff09;是三种常见的应用部署和更新策略。下面将分别对这几种发布方式进行说明&#xff0c;并给出相应的例子。 蓝绿发布 蓝绿发布是一种无缝切换版本的部署策…...