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

5.网络编程-socker(golang版)

目录

一、什么是socket?

二、Golang中使用TCP

TCP服务端

TCP客户端​​​​​​​

三、TCP黏包,拆包

1.什么是粘包,拆包?

2.为什么UDP没有粘包,拆包?

3.粘包拆包发生场景

4.TCP黏包

黏包服务端

黏包客户端

为什么会出现粘包

解决方案

自定义协议

服务端

客户端

5.TCP拆包

为什么会出现TCP拆包

常见方案

四、Golang中使用UDP

UDP服务端

UDP客户端


一、什么是socket?

Socket是BSD UNIX的进程通信机制,通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络的API,它定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。电脑上运行的应用程序通常通过”套接字”向网络发出请求或者应答网络请求。

Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。

网络编程即socket编程

二、Golang中使用TCP

在Go语言中使用Socket编程,通常指的是使用标准库net包中的net.TCPConnnet.UDPConn接口来实现TCP或UDP网络通信。以下分别给出一个简单的TCP服务器和客户端示例:

TCP服务端

package mainimport ("bufio""fmt""log""net"
)func handleConnection(conn net.Conn) {defer conn.Close() // 关闭连接for {reader := bufio.NewReader(conn)var buf [128]byten, err := reader.Read(buf[:]) // 读取数据if err != nil {fmt.Println("read from client failed, err:", err)break}recvStr := string(buf[:n])fmt.Println("收到client端发来的数据:", recvStr)conn.Write([]byte(recvStr)) // 发送数据}
}func main() {listener, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}defer listener.Close()log.Println("Listening for connections on :8080...")for {conn, err := listener.Accept()if err != nil {log.Println("Error accepting connection:", err)continue}go handleConnection(conn) // 在新的goroutine中处理每个连接}
}

TCP客户端

package mainimport ("bufio""fmt""log""net""os""strings"
)func main() {conn, err := net.Dial("tcp", "localhost:8080")if err != nil {log.Fatal(err)}defer conn.Close()log.Println("Connected to localhost:8080")inputReader := bufio.NewReader(os.Stdin)for {input, _ := inputReader.ReadString('\n') // 读取用户输入inputInfo := strings.Trim(input, "\r\n")if strings.ToUpper(inputInfo) == "Q" { // 如果输入q就退出return}_, err = conn.Write([]byte(inputInfo)) // 发送数据if err != nil {return}buf := [512]byte{}n, err := conn.Read(buf[:])if err != nil {fmt.Println("recv failed, err:", err)return}fmt.Printf("recv server msg: %s", buf[:n])}
}

以上示例展示了如何使用Go语言创建一个简单的TCP服务器和客户端。服务器监听8080端口,接受客户端连接并转发数据。客户端连接到本地的8080端口,发送一个消息并接收服务器的响应。

对于UDP编程,可以使用net.ListenUDP创建UDP监听器,net.DialUDP创建UDP连接,以及net.UDPConn接口进行数据收发。具体的代码实现与上述TCP示例类似,但需要注意UDP是无连接的、不可靠的传输协议,相应的编程模型和错误处理会有所不同。

三、TCP黏包,拆包

1.什么是粘包,拆包?

  • TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中,比如RPC框架。
  • 在使用TCP进行数据传输时,由于TCP是基于字节流的协议,而不是基于消息的协议,可能会出现粘包(多个消息粘在一起)和拆包(一个消息被拆分成多个部分)的问题。这些问题可能会导致数据解析错误或数据不完整。

2.为什么UDP没有粘包,拆包?

UDP(User Datagram Protocol)没有粘包和拆包现象,这是由其设计特性和工作方式所决定的。以下是详细解释:

  1. 面向数据报的传输方式

    • UDP是一种面向数据报的协议,每个数据报(datagram)都是一个独立、完整的信息单元,具有明确的边界。每个数据报包含源端口、目的端口、长度、校验和等信息,以及用户数据。发送端将数据封装成一个个数据报发送,接收端则按数据报的完整单位接收,不存在数据报之间的合并或拆分。
  2. 无连接状态

    • UDP是无连接的协议,发送数据前无需建立连接,发送后也不维持连接状态。每个数据报的发送与接收都是独立的事件,没有前后关联。因此,不存在因连接状态导致的数据包合并(即“粘包”)。
  3. 无顺序保证、重传机制

    • UDP不保证数据报的顺序传递,也不进行数据重传。每个数据报在网络中独立传输,可能因为网络条件等因素导致乱序、丢失或重复,但这些都由应用层自行处理。由于每个数据报都是单独处理的,不会因为等待其他数据报而被“粘”在一起,也不会因为重传而“拆包”。
  4. 固定报头长度

    • UDP数据报的报头长度是固定的(8字节),没有可变长度的选项字段,这使得接收端可以很容易地定位到用户数据的起始位置,无需担心因为解析报头时遇到“粘包”问题。
  5. 无流控和拥塞控制

    • TCP具有滑动窗口、流量控制和拥塞控制机制,这些机制可能导致发送端积攒多个小数据包合并成一个大的TCP段发送,或者在接收端因窗口大小限制而暂时缓存数据,从而形成“粘包”。而UDP没有这些复杂的控制机制,数据报一旦发送出去,就不受发送端或接收端的流量控制和拥塞控制影响,不会发生“粘包”或“拆包”。

综上所述,由于UDP的面向数据报、无连接状态、无顺序保证、无重传、固定报头长度以及无流控和拥塞控制等特性,每个UDP数据报在发送和接收时都是独立处理的,具有明确的边界,不会与其他数据报合并(“粘包”)或需要拆分(“拆包”)。应用程序使用UDP时,需要自行处理数据完整性、顺序和重传等问题。

3.粘包拆包发生场景

因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。

  • 如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。
  • 如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包。

关于粘包和拆包可以参考下图的几种情况:

  • 理想状况:两个数据包逐一分开发送。
  • 粘包:两个包一同发送。
  • 拆包:Server接收到不完整的或多出一部分的数据包。

4.TCP黏包

黏包服务端

package mainimport ("bufio""fmt""io""net"
)// socket_stick/server/main.gofunc process(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)var buf [1024]bytefor {n, err := reader.Read(buf[:])if err == io.EOF {break}if err != nil {fmt.Println("read from client failed, err:", err)break}recvStr := string(buf[:n])fmt.Println("收到client发来的数据:", recvStr)}
}func main() {listen, err := net.Listen("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("listen failed, err:", err)return}defer listen.Close()for {conn, err := listen.Accept()if err != nil {fmt.Println("accept failed, err:", err)continue}go process(conn)}
}

黏包客户端

package mainimport ("fmt""net"
)// socket_stick/client/main.gofunc main() {conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("dial failed, err", err)return}defer conn.Close()for i := 0; i < 10; i++ {msg := `你好,吃了吗?`conn.Write([]byte(msg))}
}

客户端分10次发送的数据,在服务端并没有成功的输出10次,而是多条数据“粘”到了一起。 

为什么会出现粘包

主要原因就是tcp数据传递模式是流模式,在保持长连接的时候可以进行多次的收和发。

“粘包"可发生在发送端也可发生在接收端:

  1. 由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。
  2. 接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。

出现"粘包"的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包进行封包和拆包的操作。

封包:封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了(过滤非法包时封包会加入"包尾"内容)。包头部分的长度是固定的,并且它存储了包体的长度,根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包。

我们可以自己定义一个协议,比如数据包的前4个字节为包头,里面存储的是发送的数据的长度。

解决方案

自定义协议
package protoimport ("bufio""bytes""encoding/binary"
)// Encode 将消息编码
func Encode(message string) ([]byte, error) {// 读取消息的长度,转换成int32类型(占4个字节)var length = int32(len(message))var pkg = new(bytes.Buffer)// 写入消息头err := binary.Write(pkg, binary.LittleEndian, length)if err != nil {return nil, err}// 写入消息实体err = binary.Write(pkg, binary.LittleEndian, []byte(message))if err != nil {return nil, err}return pkg.Bytes(), nil
}// Decode 解码消息
func Decode(reader *bufio.Reader) (string, error) {// 读取消息的长度lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据lengthBuff := bytes.NewBuffer(lengthByte)var length int32err := binary.Read(lengthBuff, binary.LittleEndian, &length)if err != nil {return "", err}// Buffered返回缓冲中现有的可读取的字节数。if int32(reader.Buffered()) < length+4 {return "", err}// 读取真正的消息数据pack := make([]byte, int(4+length))_, err = reader.Read(pack)if err != nil {return "", err}return string(pack[4:]), nil
}
服务端
package mainimport ("bufio""csdn/proto""fmt""io""net"
)func process(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)for {msg, err := proto.Decode(reader)if err == io.EOF {return}if err != nil {fmt.Println("decode msg failed, err:", err)return}fmt.Println("收到client发来的数据:", msg)}
}func main() {listen, err := net.Listen("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("listen failed, err:", err)return}defer listen.Close()for {conn, err := listen.Accept()if err != nil {fmt.Println("accept failed, err:", err)continue}go process(conn)}
}
客户端
package mainimport ("csdn/proto""fmt""net"
)func main() {conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("dial failed, err", err)return}defer conn.Close()for i := 0; i < 10; i++ {msg := `你好,吃了吗?`data, err := proto.Encode(msg)if err != nil {fmt.Println("encode msg failed, err:", err)return}conn.Write(data)}
}

5.TCP拆包

为什么会出现TCP拆包

TCP拆包现象主要由以下几个原因导致:

  1. 网络传输限制 - 最大传输单元(MTU):TCP协议允许发送端将数据划分为多个数据段(segments)进行发送。这些数据段在传输过程中可能会遇到网络路径中不同设备的最大传输单元(MTU)限制。MTU是指网络层(如IP层)能够承载的最大数据包尺寸,通常以字节为单位。如果一个TCP数据段的大小超过了路径中某个设备的MTU,该设备会将数据包进行分片(fragmentation),将其分割成多个较小的数据包以便通过网络。这些分片数据包可能独立地经过网络传输,并在接收端以不同的顺序到达。尽管TCP协议会确保这些分片数据包在接收端按正确的顺序重组,但应用层接收到的数据可能不再是发送端最初连续发送的形式,从而表现为拆包。

  2. 接收端缓冲区满:接收端的TCP连接有一个缓冲区用于暂存接收到的数据。当接收端处理数据的速度赶不上数据到达的速度,或者由于其他原因导致缓冲区空间不足时,新的数据段可能会被推迟接收。这种情况下,即使原本连续的数据段在传输过程中没有被网络设备分片,也可能因为接收端暂时无法接纳而被迫分开接收,从而在应用层看起来像是发生了拆包。

  3. 网络拥塞:在网络拥塞时,数据包可能会在网络中经历更长的延迟,甚至被路由器暂时存储(队列化)。这可能导致原本连续发送的数据段到达接收端的顺序被打乱,或者由于接收端TCP窗口大小限制,只能接收部分数据,从而出现拆包现象。

综上所述,TCP拆包主要源于网络传输中的MTU限制导致的数据分片、接收端缓冲区处理能力的限制,以及网络拥塞等因素。为应对拆包问题,通常需要在应用层设计合适的协议或策略,如消息定界、使用更高层协议、合理设置接收缓冲区大小、实现应用层重传机制等。

常见方案

  1. 固定长度:发送端将每个消息固定为相同的长度,接收端按照固定长度进行拆包。这样可以确保每个消息的长度是一致的,但是对于不同长度的消息可能会浪费一些空间。
  2. 分隔符:发送端在每个消息的末尾添加一个特殊的分隔符(比如换行符或特殊字符),接收端根据分隔符进行拆包。这种方法适用于消息中不会出现分隔符的情况。
  3. 消息长度前缀:发送端在每个消息前面添加一个固定长度的消息长度字段,接收端先读取消息长度字段,然后根据长度读取相应长度的数据。这种方法可以准确地拆分消息,但需要保证消息长度字段的一致性。

四、Golang中使用UDP

在Go语言中使用UDP(User Datagram Protocol)进行网络通信,您可以使用标准库net包提供的net.UDPConn接口。以下是一个简单的UDP服务器和客户端示例:

UDP服务端

package mainimport ("fmt""net"
)func main() {// 监听指定的UDP端口serverAddr, err := net.ResolveUDPAddr("udp", ":8080")if err != nil {fmt.Println("Error resolving address:", err)return}listener, err := net.ListenUDP("udp", serverAddr)if err != nil {fmt.Println("Error listening:", err)return}defer listener.Close()fmt.Println("UDP server listening on :8080")buf := make([]byte, 1024)for {n, remoteAddr, err := listener.ReadFromUDP(buf)if err != nil {fmt.Println("Error reading:", err)continue}message := buf[:n]fmt.Printf("Received message from %v: %s\n", remoteAddr, message)// 处理消息并回复(可选)response := "Hello, UDP client!"_, err = listener.WriteToUDP([]byte(response), remoteAddr)if err != nil {fmt.Println("Error writing:", err)}}
}

UDP客户端

package mainimport ("fmt""net"
)func main() {serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8080")if err != nil {fmt.Println("Error resolving address:", err)return}conn, err := net.DialUDP("udp", nil, serverAddr)if err != nil {fmt.Println("Error connecting:", err)return}defer conn.Close()message := "Hello, UDP server!"_, err = conn.Write([]byte(message))if err != nil {fmt.Println("Error sending:", err)return}fmt.Println("Sent message:", message)buf := make([]byte, 1024)n, _, err := conn.ReadFromUDP(buf)if err != nil {fmt.Println("Error reading:", err)return}response := buf[:n]fmt.Printf("Received response: %s\n", response)
}

在这个示例中,服务器监听8080端口,接收客户端发送的消息并打印出来,然后向客户端发送一条回复消息。客户端连接到服务器,发送一条消息并接收服务器的响应。注意,UDP是无连接的、不可靠的协议,不保证消息的顺序、可靠传输或重传,适用于对实时性要求较高但能容忍一定程度数据丢失的应用场景。在实际使用时,可能需要根据应用需求添加适当的错误处理、重传机制和数据校验等逻辑。

相关文章:

5.网络编程-socker(golang版)

目录 一、什么是socket&#xff1f; 二、Golang中使用TCP TCP服务端 TCP客户端​​​​​​​ 三、TCP黏包&#xff0c;拆包 1.什么是粘包&#xff0c;拆包&#xff1f; 2.为什么UDP没有粘包&#xff0c;拆包&#xff1f; 3.粘包拆包发生场景 4.TCP黏包 黏包服务端 …...

网格矢量如何计算莫兰指数

网格矢量如何计算莫兰指数 引言 遇到一个问题&#xff0c;计算矢量网格的莫兰指数。 概念解释 莫兰指数 莫兰指数&#xff08;Moran’s Index&#xff09;是一种空间自相关指标&#xff0c;用于衡量空间数据的相似性和聚集程度。它可以用来描述一个区域与其邻近区域之间的属…...

《containerd原理剖析与实战》大模型时代下如何学习云原生

大模型与云原生 近年来&#xff0c;大语言模型的热度可谓是愈发高涨&#xff0c;尤其是今年年初 Sora 的出现&#xff0c;更是让全球再次看到了AIGC 的巨大威力。 Sora 生成实例视频---几头巨大的长毛猛犸踏着积雪的草地而来 在当前大模型流行的时代下&#xff0c;云原生技术…...

【实用工具】使用飞书机器人监控工程日志

1.创建一个飞书群聊&#xff0c;设置-->群机器人-->添加机器人-->自定义机器人-->修改机器人名称-->添加 2.复制webhook地址 3.编写日志请求代码 import logging import requests import json import os from datetime import datetime import time import sub…...

NIKKE胜利女神PC怎么设置中文 手把手教你设置中文教程

这个游戏中的妮姬分四个企业&#xff0c;其中朝圣者这个派别的妮姬很少而且不在愿望单理&#xff0c;朝圣者的所有姐姐都很哇塞&#xff0c;红莲更是其中的大姐大。一般想抽朝圣者只能靠歪或者出限定卡池&#xff0c;举个栗子&#xff0c;我入坑的时候 朝圣者 神罚 是限定卡池&…...

【leetcode面试经典150题】2.移除元素(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…...

实现几何对象按照一定距离向外缓冲

1、首先&#xff0c;确保你已经引入了Turf.js库。你可以通过在HTML文件中添加以下代码来引入 <script src"https://cdn.jsdelivr.net/npm/turf/turf6.5.0/turf.min.js"></script>2、使用turf.buffer实现几何对象按照设定距离扩充 let originalCoordinat…...

现代深度学习模型和技术

Transformer模型的理解和应用 Transformer模型自2017年由Vaswani等人在论文《Attention is All You Need》中提出以来&#xff0c;已经彻底改变了自然语言处理&#xff08;NLP&#xff09;领域的面貌。Transformer的核心是自注意力&#xff08;Self-Attention&#xff09;机制…...

go的orm框架-Gorm

官网文档 特点 全功能 ORM 关联 (拥有一个&#xff0c;拥有多个&#xff0c;属于&#xff0c;多对多&#xff0c;多态&#xff0c;单表继承) Create&#xff0c;Save&#xff0c;Update&#xff0c;Delete&#xff0c;Find 中钩子方法 支持 Preload、Joins 的预加载 事务&…...

嵌入式开发学习---(部分)数据结构(无代码)

数据结构 为什么学习数据结构&#xff1f; 1&#xff09;c语言告诉如何写程序&#xff0c;数据结构是如何简洁高效的写程序 2&#xff09;遇到一个实际问题&#xff0c;需要写程序去实现相应功能&#xff0c;需要解决那两个方面的问题&#xff1f; 如何表达数据之间的逻辑规律…...

ChatGPT 之联盟营销

原文&#xff1a;ChatGPT for Affiliate Marketing 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第二章 制定转化对话 制定转化对话是每个营销人员和企业所有者都应该掌握的关键技能。它涉及创建和传递引人入胜的信息&#xff0c;吸引您的受众并激励他们采取行动。…...

1.k8s简介

目录 k8s是什么 k8s不是什么 云原生 微服务 整体式架构与微服务架构 微服务的特性 微服务的优势 k8s是什么 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快…...

go包下载时报proxyconnect tcp: dial tcp 127.0.0.1:80: connectex错误的解决方案

一大早的GoLand就开始抽风了&#xff0c;好几个文件import都红了&#xff0c;于是我正常操作点击提示的sync&#xff0c;但是却报了一堆错&#xff1a; go: downloading google.golang.org/grpc v1.61.1 go: downloading google.golang.org/genproto v0.0.0-20240228224816-df9…...

Vaadin框架是如何处理前后端交互的?列举几个Vaadin中常用的UI组件,并描述它们的作用。如何使用Vaadin的布局管理器来构建复杂的用户界面?

Vaadin框架是如何处理前后端交互的&#xff1f; Vaadin框架处理前后端交互的方式主要基于服务端渲染和事件驱动的编程模型。以下是具体的处理过程&#xff1a; 服务端渲染&#xff1a;Vaadin应用程序的UI组件是在服务器端创建和渲染的。当用户在浏览器中访问应用程序时&#x…...

动态属性的响应式问题和行内编辑的问题

动态属性的响应式问题 通过点击给目标添加动态数据&#xff0c;该数据不具备响应式特性 如下图&#xff1a; 点击编辑&#xff0c;前面的数据框会变成输入框&#xff0c;点取消会消失 // 获取数据 async getList () {const res await xxxthis.list res.data.rows// 1. 获…...

微信小程序第六次课(模块化和绑定事件)

模块化 1.首先 我们在utils里面创建一个新的js文件 2.新的js文件里面写我们要实现的函数功能 3.把新的函数功能 通过 module.export.对外公开文件名 新文件名 的方式把之前的函数公开到其他他模块 &#xff08;类似于public 让别的模块可以…...

【Unity添加远程桌面】使用Unity账号远程控制N台电脑

设置地址&#xff1a; URDP终极远程桌面&#xff1b;功能强大&#xff0c;足以让开发人员、设计师、建筑师、工程师等等随时随地完成工作或协助别人https://cloud-desktop.u3dcloud.cn/在网站登录自己的Unity 账号上去 下载安装被控端安装 保持登录 3.代码添加当前主机 "…...

maven的settings.xml、pom.xml配置文件

1、配置文件 maven的配置文件主要有 settings.xml 和pom.xml 两个文件。 其中在maven安装目录下的settings.xml&#xff0c;如&#xff1a;D:\Program Files\apache-maven-3.6.3\conf\settings.xml 是全局配置文件 用户目录的.m2子目录下的settings.xml&#xff0c;如&#…...

使用MQTT.fx接入新版ONENet(24.4.8)

新版ONENet使用MQTT.fx 模拟接入 目录 新版ONENet使用MQTT.fx 模拟接入开始前的准备创建产品设备获取关键参数 计算签名使用MQTT.fx连接服务器数据流准备与上传数据流准备数据发送与接收 开始前的准备 创建产品 设备下载Token签名工具生成签名 创建产品设备 根据以下内容填写…...

Selenium 自动化遇见 shadow-root 元素怎么处理?

shadow-root是前端的特殊元素节点&#xff0c;其使用了一个叫做shadowDOM的技术做了封装&#xff0c;shadowDOM的作用可以理解为在默认的DOM结构中又嵌套了一个DOM结构&#xff08;和iframe有点类似&#xff0c;只不过iframe内嵌的是HTML&#xff09;&#xff0c;我们遇见shado…...

软件系统质量属性_2.面向架构评估的质量属性

为了评价一个软件系统&#xff0c;特别是软件系统的架构&#xff0c;需要进行架构评估。在架构评估过程中&#xff0c;评估人员所关注的是系统的质量属性。评估方法所普遍关注的质量属性有&#xff1a;性能、可靠性、可用性、安全性、可修改性、功能性、可变性、互操作性。 1.…...

设计模式:抽象工厂

定义 抽象工厂模式是一种创建型设计模式&#xff0c;它提供了一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。这种模式特别适用于处理产品族&#xff0c;但在不可能修改的情况下扩展产品族是困难的。 应用场景 抽象工厂模式通…...

【环境搭建】ubuntu工作站搭建全流程(显卡4090)

安装ubuntu22.04系统 首先&#xff0c;先压缩windows分区&#xff0c;按住Win X快捷键&#xff0c;选择磁盘管理,压缩分区&#xff0c;压缩出新的分区用于安装ubuntu22.04 windows插入系统盘&#xff0c;点击重启&#xff0c;一直按F12,选择系统盘启动方式语言选择chinese–…...

蓝桥杯每日一题:有序分数(递归)

给定一个整数 N&#xff0c;请你求出所有分母小于或等于 N&#xff0c;大小在 [0,1] 范围内的最简分数&#xff0c;并按从小到大顺序依次输出。 例如&#xff0c;当 N5 时&#xff0c;所有满足条件的分数按顺序依次为&#xff1a; 0/1,1/5,1/4,1/3,2/5,12/,35,2/3,3/4,4/5,1/…...

SpringBoot学习之Kibana下载安装和启动(Mac版)(三十二)

一、简介 Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。Kibana与Elasticsearch的交互方式是各种不同的图表、表格、地图等,直观的展示数据,从而达到高级的数据分析与可视化的目的。 …...

Mac下Docker Desktop starting的解决方法

记录下自己在新增了一个新的容器后&#xff0c;Disk Size过大导致启动Docker Desktop会一直卡在Docker Desktop starting&#xff0c;并且重启无效的解决方法。该方法无需重新卸载&#xff0c;并且能保留原有的镜像和容器。 一、确认问题 首先确认Docker.raw大小以确认是否和笔…...

Leetcode面试经典150_Q80删除有序数组中的重复项 II

题目&#xff1a; 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件…...

android 使用ollvm混淆so

使用到的工具 ndk 21.1.6352462&#xff08;android studio上下载的&#xff09;cmake 3.10.2.4988404&#xff08;android studio上下载的&#xff09;llvm-9.0.1llvm-mingw-20230130-msvcrt-x86_64.zipPython 3.11.5 环境配置 添加cmake mingw环境变量如下图: 编译 下载…...

Swift:在 Win10 上编程入门

访问 https://swift.org/download/ 找到 Windows 10&#xff1a;x86_64 下载 swift-5.10-RELEASE-windows10.exe 大约490MB 建议安装在 D:\Swift\ &#xff0c;安装后大约占2.56GB 官网文档&#xff1a;https://www.swift.org/documentation/ 中文教程&#xff1a;The swift…...

Linux多进程通信(4)——消息队列从入门到实战!

Linux多进程通信总结——进程间通信看这一篇足够啦&#xff01; 1.基本介绍 1&#xff09;消息队列的本质其实是一个内核提供的链表&#xff0c;内核基于这个链表&#xff0c;实现了一个数据结构&#xff0c;向消息队列中写数据&#xff0c;实际上是向这个数据结构中插入一个…...