go语言网络编程
- 网络编程
- Go语言网络编程相关API
- Go语言网络编程架构
- Go语言的网络编程实现基于以下几个关键原理:
- bufio
- bufio 包的主要功能和使用场景
- 主要类型
- 示例 tcp通信
- 解决粘包
- 粘包和拆包的产生原因
- 解决方法
- 示例
网络编程
Go语言网络编程相关API
1.1 net包net.Listen(network, address string): 创建一个网络监听器,等待进入的连接。它的第一个参数是网络类型(如"tcp"、"udp"等),第二个参数是要监听的地址。net.Dial(network, address string): 连接到指定的地址,返回一个连接对象。net.Conn接口: 提供了与网络连接相关的方法,包括:Read(b []byte) (n int, err error): 从连接中读取数据。Write(b []byte) (n int, err error): 向连接写入数据。Close() error: 关闭连接。net.Listener接口: 用于接受传入连接的接口,包含方法:Accept() (Conn, error): 接受一个连接请求。1.2 net/http包http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): 注册处理函数,当请求的URL匹配指定模式时,执行该处理函数。http.ListenAndServe(addr string, handler Handler) error: 启动HTTP服务器并监听指定地址上的请求。http.Request结构体: 包含请求的各种信息,如方法、URL、头部信息等。http.ResponseWriter接口: 用于构造HTTP响应,包含方法来设置响应头和写入响应体。http.Get(url string): 发起一个GET请求并返回响应。
Go语言网络编程架构
Go的网络编程架构主要基于事件驱动和goroutine的并发模型。
每当一个新的连接被接受时,服务器会为其启动一个新的goroutine来处理该连接。
因此,Go语言能够轻松地处理大量的并发连接,而无需使用复杂的线程管理。
服务器架构:
监听端口:服务器通过net.Listen监听指定端口。
接受连接:使用listener.Accept()接收连接。
处理连接:为每个连接创建goroutine,执行自定义的处理逻辑。
关闭连接:在处理完成后,关闭连接释放资源。
客户端架构:
创建连接:通过net.Dial或http.Get创建连接。
发送请求:向服务器发送请求或数据。
接收响应:获取服务器的响应数据。
关闭连接:处理完成后,关闭连接。
Go语言的网络编程实现基于以下几个关键原理:
Goroutine和通道: Go的并发模型建立在轻量级goroutine之上。
每一个网络连接都可以在单独的goroutine中处理,这样可以有效利用系统资源,提高并发处理能力。
通道(channel)用于在多个goroutine之间传递数据,保证数据的安全和同步。
I/O多路复用: Go使用操作系统的I/O多路复用机制,如epoll(Linux)或kqueue(BSD),
来处理大量连接的读写事件,从而减少线程上下文切换的开销。
封装性和易用性: Go的net和net/http包提供了高层次的抽象,简化了网络编程的复杂性。
开发者不需要深入底层的socket编程,只需调用简单的API即可实现复杂的网络操作。
bufio
bufio 是 Go 语言标准库中的一个包,用于提供 buffered I/O(缓冲输入/输出)功能。
它通过对输入和输出操作进行缓冲处理,从而提高程序的性能,
减少系统调用的次数。bufio 常用于处理文件、网络连接等 I/O 操作。
bufio 包的主要功能和使用场景
提高效率:
bufio 使用内存中的缓冲区来存储数据,从而减少直接的系统调用次数(如读写磁盘或网络),提高了读写效率。
简单的接口:
bufio 提供了简单易用的接口,让开发者能够更方便地处理文本和二进制数据。
处理文本数据:
bufio 特别适合处理行或单词输入输出,可以方便地读取和写入文本数据。
主要类型
以下是 bufio 包中几个重要的类型:
Reader:bufio.Reader 是一个结构体,用于缓存输入。通过 Read() 方法,可以从底层的 io.Reader 中读取数据,使用缓冲可以减少调用次数。
常用方法:Read(p []byte) (n int, err error): 从缓冲区读取数据到切片 p 中。ReadString(delim byte) (string, error): 读取直到遇到分隔符 delim 的数据,并返回作为字符串。ReadBytes(delim byte) ([]byte, error): 与 ReadString 类似,但返回字节切片。示例:reader := bufio.NewReader(conn)message, err := reader.ReadString('\n') // 读取一行数据Writer:bufio.Writer 是一个结构体,用于缓存输出。可以将数据写入缓冲区,并在缓冲区填满后再一次性写入到底层的 io.Writer。
常用方法:Write(p []byte) (n int, err error): 将数据写入缓冲区。Flush() error: 将缓冲区中的数据写入到底层的 io.Writer。
示例:writer := bufio.NewWriter(conn)writer.Write([]byte("Hello, Client!\n")) // 写入数据到缓冲区writer.Flush() // 确保将数据写入连接Scanner:bufio.Scanner 提供了一个方便的方式来逐行读取输入,常用于处理文本数据。
常用方法:Scan() bool: 读取下一个 token,返回 true 表示成功。Text() string: 返回上一个 token 作为字符串。
示例:scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() {line := scanner.Text() // 得到输入的一行fmt.Println(line)}
示例 tcp通信
这段代码实现了一个简单的聊天服务器,能够接收并广播消息到所有连接的客户端。
使用了 goroutines 来处理每个客户端的连接,
确保服务器能够同时处理多个连接。
通过 sync.Mutex 来保护对共享数据结构 clients 的并发访问。package mainimport ("bufio" // 导入 bufio 包,用于读取输入"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"sync" // 导入 sync 包,用于同步操作
)// 定义全局变量
var (// 保存所有连接的客户端,使用 map 结构clients = make(map[net.Conn]bool)clientsMux sync.Mutex // 保护 clients 的并发访问,防止数据竞态
)func main() {// 监听指定的端口(8080)listener, err := net.Listen("tcp", ":8080")if err != nil {// 如果监听失败,打印错误信息并返回fmt.Println("Error starting server:", err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println("Chat server started on :8080") // 服务器启动成功提示for {// 接受新的连接conn, err := listener.Accept()if err != nil {// 如果接受连接失败,打印错误信息并继续下一次循环fmt.Println("Error accepting connection:", err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] = true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接,并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()// 创建一个读取器reader := bufio.NewReader(conn)for {// 读取客户端发送的消息直到换行符message, err := reader.ReadString('\n')if err != nil {// 如果读取失败,打印错误信息并返回fmt.Println("Error reading from connection:", err)return}// 打印接收到的消息fmt.Printf("Received: %s", message)// 广播消息到所有其他客户端broadcastMessage(message, conn) // 调用广播函数}
}// 广播消息给所有连接的客户端
func broadcastMessage(message string, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 遍历所有连接的客户端for client := range clients {// 不向发送消息的客户端发送消息if client != sender {_, _ = client.Write([]byte(message)) // 发送消息}}
}
这段代码实现了一个简单的聊天客户端,能够连接到服务器并发送消息,同时也会接收并显示服务器发来的消息。
通过 goroutines 来异步处理接收消息和发送消息,确保用户可以一边发送消息一边接收来自服务器的消息。
使用 bufio.Scanner 和 bufio.Reader 来处理输入和输出的读取。
package mainimport ("bufio" // 导入 bufio 包,用于读取输入"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"os" // 导入 os 包,用于与操作系统交互
)func main() {// 连接到聊天服务器,指定服务器地址(localhost:8080)conn, err := net.Dial("tcp", "localhost:8080")if err != nil {// 如果连接失败,打印错误信息并返回fmt.Println("Error connecting to server:", err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息
func readMessages(conn net.Conn) {reader := bufio.NewReader(conn) // 创建一个读取器for {// 读取服务器发送的消息直到换行符message, err := reader.ReadString('\n')if err != nil {// 如果读取失败,打印错误信息并返回fmt.Println("Error reading from server:", err)return}// 打印接收到的消息fmt.Print("Received: ", message)}
}// 发送消息到服务器
func sendMessages(conn net.Conn) {scanner := bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println("消息发送中:") // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message := scanner.Text()// 发送消息到服务器,并在结尾加上换行符_, err := conn.Write([]byte(message + "\n"))if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending message:", err)return}}
}
解决粘包
在网络编程中,尤其是使用TCP进行通信时,常会遇到“粘包”与“拆包”现象。这是因为TCP是一个流式协议,
数据在传输过程中可能会被合并成一个大的数据包,或者一个大的数据包可能被分割成多个小的数据包,从而导致接收方无法正确解析消息。
粘包和拆包的产生原因
粘包:发送方可能连续发送多个消息,而TCP将这些消息合并为一个包,接收方收到的数据中包含多个消息。
拆包:发送方发送一个较大的消息,TCP将其分成多个小包发送,接收方在一次读取中只获取到部分消息。
解决方法
为了防止粘包和拆包现象,通常采用以下几种策略来处理:
-
使用固定长度的消息
在这种方法中,每个消息都使用固定的字节长度来进行编码。这样接收方可以根据固定的长度来读取数据。优点:实现简单。
缺点:不适用于消息长度不一致的情形,浪费带宽(如果消息较短)。 -
使用分隔符
在每个消息的末尾加入一个特定的分隔符,比如换行符、特定字符等,接收方在读取数据时,可以根据分隔符进行解析。优点:可以处理可变长度的消息。
缺点:需要确保分隔符不会出现在消息内容中。 -
消息头部长度
在每个消息前添加一个固定大小的头部,头部包含消息的长度信息,接收方首先读取头部,获取消息长度,再按长度读取数据。优点:适用于任意长度的消息。
缺点:稍显复杂,需处理头部。
示例
此代码实现了一个聊天服务器,能够接受多个客户端连接,
接收消息并将其广播给所有连接的客户端。
它通过使用二进制数据处理来确保消息的完整性和准确性,使用协程来并发处理每个连接,
确保服务器在处理多个客户端时的高效性。
通过使用 sync.Mutex 来避免对共享数据结构 clients 的竞争访问。
package mainimport ("encoding/binary" // 导入 encoding/binary 包,用于在网络中进行字节序转换"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"sync" // 导入 sync 包,用于同步操作
)// 定义全局变量
var (clients = make(map[net.Conn]bool) // 用于保存所有连接的客户端clientsMux sync.Mutex // 保护 clients 的并发访问
)func main() {// 监听指定的端口(8080)listener, err := net.Listen("tcp", ":8080")if err != nil {// 如果启动失败,打印错误信息并返回fmt.Println("Error starting server:", err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println("Chat server started on :8080") // 服务器启动成功提示for {// 接受新的连接conn, err := listener.Accept()if err != nil {// 如果接受连接失败,打印错误信息并继续下一次循环fmt.Println("Error accepting connection:", err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] = true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接,并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()for {// 读取消息长度(前4个字节)lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区_, err := conn.Read(lengthBuffer) // 从连接中读取消息长度if err != nil {// 如果读取长度失败,打印错误信息并返回fmt.Println("Error reading length:", err)return}// 根据读取到的长度转换成整型msgLength := int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer := make([]byte, msgLength) // 根据长度创建消息缓冲区// 读取消息/* @param messageBuffer 接收消息的缓冲区 @param conn 客户端连接 @return 返回读取的字节数和错误信息*/_, err = conn.Read(messageBuffer) // 从连接中读取消息if err != nil {// 如果读取消息失败,打印错误信息并返回fmt.Println("Error reading message:", err)return}// 打印接收到的消息fmt.Printf("Received message: %s\n", string(messageBuffer))// 将接收到的消息广播给所有其他客户端broadcastMessage(messageBuffer, conn)}
}// 广播消息给所有连接的客户端
func broadcastMessage(message []byte, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 获取消息长度msgLength := uint32(len(message))lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 遍历所有连接的客户端for client := range clients {// 不向发送者发送消息if client != sender {// 首先发送消息长度_, _ = client.Write(lengthBuffer) // 发送长度_, _ = client.Write(message) // 然后发送消息}}
}
此代码实现了一个简单的聊天客户端,能够连接到服务器、发送消息并接收服务器的消息。
通过二进制传输消息长度,确保了消息的完整性。
package mainimport ("bufio" // 导入 bufio 包,用于读取输入"encoding/binary" // 导入 encoding/binary 包,用于字节序转换"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"os" // 导入 os 包,用于与操作系统交互
)func main() {// 连接到聊天服务器,指定服务器地址(localhost:8080)conn, err := net.Dial("tcp", "localhost:8080")if err != nil {// 如果连接失败,打印错误信息并返回fmt.Println("Error connecting to server:", err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息的函数
func readMessages(conn net.Conn) {for {// 创建一个4字节的缓冲区用于读取消息长度lengthBuffer := make([]byte, 4)_, err := conn.Read(lengthBuffer) // 从连接中读取消息长度if err != nil {// 如果读取长度失败,打印错误信息并返回fmt.Println("Error reading length:", err)return}// 将读取到的长度转换为整型msgLength := int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer := make([]byte, msgLength) // 根据长度创建消息缓冲区_, err = conn.Read(messageBuffer) // 从连接中读取消息if err != nil {// 如果读取消息失败,打印错误信息并返回fmt.Println("Error reading message:", err)return}// 打印接收到的消息fmt.Printf("Received message: %s\n", string(messageBuffer))}
}// 发送消息到服务器的函数
func sendMessages(conn net.Conn) {scanner := bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println("Type your messages below (end with Enter):") // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message := scanner.Text()// 获取消息长度msgLength := uint32(len(message))lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 先发送消息长度_, err := conn.Write(lengthBuffer) // 发送长度if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending length:", err)return}// 再发送消息_, err = conn.Write([]byte(message)) // 发送消息if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending message:", err)return}}
}相关文章:
go语言网络编程
网络编程Go语言网络编程相关APIGo语言网络编程架构Go语言的网络编程实现基于以下几个关键原理:bufiobufio 包的主要功能和使用场景主要类型示例 tcp通信解决粘包粘包和拆包的产生原因解决方法示例 网络编程 Go语言网络编程相关API 1.1 net包net.Listen(network, a…...
LeetcodeLCR 116. 省份数量
文章目录 题目原题链接思路C代码 题目 原题链接 LCR 116. 省份数量 思路 利用并查集的思想,将连接的诚实放在一个集合当中,最后遍历并查集数组判断有几颗树 初始化一个并查集;将连通的城市合并;统计并查集中树的个数;…...
Linux系统上搭建Vulhub靶场
Linux系统上搭建Vulhub靶场 vulhub 是一个开源的漏洞靶场,它提供了各种易受攻击的服务和应用程序,供安全研究人员和学习者测试和练习。要在 Linux 系统上安装和运行 vulhub,可以按照以下步骤进行: 1. 安装 Docker 和 Docke…...
Avalonia的第三方UI库SukiUI详细教程
文章目录 一、SukiUI 简介二、安装与配置1、安装 SukiUI 库:2、配置 Avalonia 项目以使用 SukiUI:三、基本组件使用1、按钮(SukiButton):2、文本框(SukiTextBox):3、标签(SukiLabel):4、下拉列表(SukiComboBox):四、布局与容器1、布局容器介绍:2、使用布局容器组…...
https协议文件上传比http协议慢
一.自己写一个文件上传的接口,在浏览器文件上传https协议比http协议慢(速度上https协议是http协议的八分之一左右),在postman上传是正常的(证明代码是没有问题的),那就是协议的问题 二.经发现&…...
Elasticsearch在大数据处理中的优势
Elasticsearch 在大数据处理中的优势主要体现在以下几个方面: 1. 分布式架构 水平扩展:Elasticsearch 设计为分布式系统,可以轻松地通过增加节点来水平扩展,处理 PB 级别的数据。数据分片和复制:数据自动分片并跨多个…...
cmake--target_compile_definitions
作用 笼统的说是:该命令添加预编译选项到编译目标中。 预编译选项 预编译选项(Preprocessor Options)是一类用于控制 C/C 预处理器行为的编译选项。预处理器是 C/C 编译过程中的第一个处理阶段,主要负责对源代码中的预处理指令…...
MATLAB数据文件读写:1.格式化读写文件
格式化读写文件 matlab提供了对数据文件建立、打开、读取、写入、关闭等操作的函数。 数据文件可以分为两类: 文本文件:以ASCII码形式存储的文本文件;编码基于字符定长,译码相对容易二进制文件:以二进制形式存储的文…...
NFTScan | 09.16~09.23 NFT 市场热点汇总
欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期:2024.09.16~ 2024.09.22 NFT Hot News 01/ DeGods 推出代币 DEGOD,用户可通过 DeGods、y00ts 或 DUST 进行转换 9 月 16 日,Solana NFT 项目 DeGods 推出代币…...
rabbitmq整合skywalking并编写自定义插件增强
rabbitmq整合skywalking 首先先下载准备好skywalking 的服务端和ui控制台,java-agent https://skywalking.apache.org/downloads/ 整合skywalking 我的流程是在生产者和消费者服务中去引入一个mq的sdk,具体SDK的内容可以查看这篇文章 在sdk的pom文件…...
sftp登录ipv6用中括号 `sftp x@[ipv6]`
sftp登录ipv6用中括号 sftp x[ipv6] 实例 sftp root[2::fd40:1:1]SFTP(Secure File Transfer Protocol,安全文件传输协议)是一种基于SSH(Secure Shell)的安全协议,用于在网络上安全地传输文件。当需要登录…...
Python 从入门到实战25(模块)
我们的目标是:通过这一套资料学习下来,通过熟练掌握python基础,然后结合经典实例、实践相结合,使我们完全掌握python,并做到独立完成项目开发的能力。 上篇文章我们讨论了类继承的相关知识。今天我们将学习一下模块的…...
Leetcode面试经典150题-172.阶乘后的零
给定一个整数 n ,返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 示例 1: 输入:n 3 输出:0 解释:3! 6 ,不含尾随 0示例 2: 输入:n 5 输出&a…...
【机器学习】揭秘GBDT:梯度提升决策树
目录 🍔 提升树 🍔 梯度提升树 🍔 举例介绍 3.1 初始化弱学习器(CART树) 3.2 构建第一个弱学习器(CART树) 3.3 构建第二个弱学习器(CART树) 3.4 构建第三个弱学习…...
Android Studio 2024 安装、项目创建、加速、优化
文章目录 Android Studio安装Android Studio项目创建Android Studio加速修改GRADLE_USER_HOME位置减少C盘占用空间GRADLE加速 修改模拟器位置减少C盘占用空间参考资料 Android Studio安装 下载android studio download android-studio-2024.1.2.12-windows.exe 或者 android-…...
JSP(Java Server Pages)基础使用
首先在web文件夹中新建一个jsp/jspx文件,这个文件就是jsp文件 <%--Created by IntelliJ IDEA.User: ***Date: 2024/9/23Time: 18:43To change this template use File | Settings | File Templates. --%> <% page contentType"text/html;charsetUTF-…...
数据结构 - 概述及其术语
经过上一章节《数据结构与算法之间有何关系?》的阐述,相信大家对数据结构多少有了点了解,今天我们将进入数据结构的正式学习中。 在计算机科学中,数据结构是一种数据管理、组织和存储的格式。它是相互之间存在一种或多种特定关系的…...
UE5——在线子系统
Unreal Engine 5 (UE5) 的在线子系统(Online Subsystem)实现多人在线游戏的原理涉及到网络编程和分布式系统设计中的多个方面。以下是该系统工作的一些核心概念和技术: 1. 客户端-服务器架构: - 大多数现代多人在线游戏采用客户端-服务器模型…...
9.23-部署项目
部署项目 一、先部署mariadb [rootk8s-master ~]# mkdir aaa [rootk8s-master ~]# cd aaa/ [rootk8s-master aaa]# # 先部署mariadb [rootk8s-master aaa]# # configmap [rootk8s-master aaa]# vim mariadb-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: ma…...
非标独立设计选型--二十六--电磁阀的选型件算
电磁阀:电磁控制---自动化的关键 PLC ---- 继电器----电磁阀----调速阀----气缸 供气源--- 【电磁阀主要负责:换向,实现气缸的动作变化】 电磁阀有哪些参数是会影响到使用的? …...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
