【golang】网络数据包捕获库 gopacket

详解 github.com/google/gopacket/pcap

github.com/google/gopacket/pcap 是 Go 语言中一个强大的网络数据包捕获库,它是 gopacket 项目的一部分,提供了对 libpcap(Linux/Unix)和 WinPcap(Windows)的 Go 语言绑定,用于实时网络数据包捕获和分析。

核心功能

  1. 实时网络数据包捕获
  2. 过滤网络流量(BPF过滤器)
  3. 读取和解析pcap文件
  4. 统计网络接口信息

基本使用

1. 安装

go get github.com/google/gopacket
go get github.com/google/gopacket/pcap

2. 获取网络接口列表

devices, err := pcap.FindAllDevs()
if err != nil {log.Fatal(err)
}for _, device := range devices {fmt.Printf("Device: %s\n", device.Name)fmt.Printf("Description: %s\n", device.Description)fmt.Printf("Flags: %d\n", device.Flags)for _, address := range device.Addresses {fmt.Printf("\tIP: %s\n", address.IP)fmt.Printf("\tNetmask: %s\n", address.Netmask)}
}

3. 打开网络接口进行捕获

handle, err := pcap.OpenLive("eth0",    // 接口名65536,     // 最大包长度true,      // 是否启用混杂模式pcap.BlockForever, // 超时时间
)
if err != nil {log.Fatal(err)
}
defer handle.Close()

4. 设置BPF过滤器

err = handle.SetBPFFilter("tcp and port 80")
if err != nil {log.Fatal(err)
}

5. 读取数据包

packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {// 处理每个数据包fmt.Println(packet)
}

高级功能

1. 解析数据包

// 解析以太网层
ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
if ethernetLayer != nil {ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
}// 解析IP层
ipLayer := packet.Layer(layers.LayerTypeIPv4)
if ipLayer != nil {ip, _ := ipLayer.(*layers.IPv4)fmt.Println("Source IP: ", ip.SrcIP)fmt.Println("Destination IP: ", ip.DstIP)
}// 解析TCP层
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {tcp, _ := tcpLayer.(*layers.TCP)fmt.Println("Source Port: ", tcp.SrcPort)fmt.Println("Destination Port: ", tcp.DstPort)
}

2. 写入pcap文件

handle, err := pcap.OpenDead(layers.LinkTypeEthernet, 65536)
if err != nil {log.Fatal(err)
}
defer handle.Close()writer, err := pcap.NewWriter(handle, "output.pcap")
if err != nil {log.Fatal(err)
}
defer writer.Close()// 创建并写入数据包
// ... (创建数据包代码)
err = writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
if err != nil {log.Fatal(err)
}

3. 读取pcap文件

handle, err := pcap.OpenOffline("input.pcap")
if err != nil {log.Fatal(err)
}
defer handle.Close()packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {fmt.Println(packet)
}

性能优化

  1. 重用缓冲区:减少内存分配

    var buf [4096]byte
    for {data, ci, err := handle.ReadPacketData()if err != nil {continue}copy(buf[:], data)// 处理数据
    }
    
  2. 零拷贝处理:直接操作原始数据

    packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
    
  3. 并发处理:使用多个goroutine处理数据包

    packets := make(chan gopacket.Packet, 1000)
    go func() {for packet := range packetSource.Packets() {packets <- packet}close(packets)
    }()for i := 0; i < runtime.NumCPU(); i++ {go func() {for packet := range packets {// 处理数据包}}()
    }
    

常见问题

  1. 权限问题:需要root或管理员权限才能捕获网络数据包
  2. 接口不可用:确保接口名称正确且处于活动状态
  3. 过滤器语法错误:BPF过滤器需要正确语法
  4. 内存泄漏:确保及时关闭handle和释放资源

实际应用示例

简单的HTTP请求捕获

handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
if err != nil {log.Fatal(err)
}
defer handle.Close()err = handle.SetBPFFilter("tcp and port 80")
if err != nil {log.Fatal(err)
}packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {appLayer := packet.ApplicationLayer()if appLayer != nil {payload := appLayer.Payload()if bytes.Contains(payload, []byte("HTTP")) {fmt.Printf("%s\n", payload)}}
}

pcap 包是 Go 中网络监控和安全工具开发的基础,结合 gopacket 的其他组件可以构建强大的网络分析工具。



使用 github.com/google/gopacket/pcap 解析 DNS 请求和响应

github.com/google/gopacket/pcap 可以捕获和解析 DNS 请求和响应,但需要结合 gopacket/layers 包中的 DNS 层解析功能。

1. 基本 DNS 解析设置

首先需要导入必要的包:

import ("github.com/google/gopacket""github.com/google/gopacket/layers" // 包含DNS层定义"github.com/google/gopacket/pcap"
)

2. 捕获 DNS 流量的配置

方法一:捕获所有DNS流量(端口53)

handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
if err != nil {panic(err)
}
defer handle.Close()// 设置BPF过滤器捕获DNS流量(UDP和TCP端口53)
err = handle.SetBPFFilter("udp port 53 or tcp port 53")
if err != nil {panic(err)
}

方法二:区分请求和响应

// 捕获发往DNS服务器的请求
err = handle.SetBPFFilter("dst port 53")// 或者捕获来自DNS服务器的响应
err = handle.SetBPFFilter("src port 53")

3. 解析DNS数据包

packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {// 检查是否包含DNS层dnsLayer := packet.Layer(layers.LayerTypeDNS)if dnsLayer != nil {dns, _ := dnsLayer.(*layers.DNS)analyzeDNS(dns) // 自定义解析函数}
}

4. 详细DNS解析函数

func analyzeDNS(dns *layers.DNS) {// 判断是请求还是响应if dns.QR {fmt.Println("[DNS Response]")} else {fmt.Println("[DNS Query]")}// 打印基本信息fmt.Printf("ID: %d, OpCode: %s, RecursionDesired: %t\n",dns.ID, dns.OpCode, dns.RD)// 解析问题部分(查询的问题)for _, question := range dns.Questions {fmt.Printf("Query: %s (Type: %s, Class: %s)\n",string(question.Name), question.Type, question.Class)}// 解析回答部分(响应的资源记录)for _, answer := range dns.Answers {fmt.Printf("Answer: %s -> %s (Type: %s, TTL: %d)\n",string(answer.Name),getAnswerValue(answer), // 自定义函数获取值answer.Type,answer.TTL)}// 解析权威名称服务器部分for _, ns := range dns.Authorities {fmt.Printf("Authority: %s -> %s\n",string(ns.Name),getAnswerValue(ns))}// 解析附加记录部分for _, extra := range dns.Additionals {fmt.Printf("Additional: %s -> %s\n",string(extra.Name),getAnswerValue(extra))}
}// 辅助函数:根据类型获取DNS记录的值
func getAnswerValue(answer layers.DNSResourceRecord) string {switch answer.Type {case layers.DNSTypeA:return answer.IP.String()case layers.DNSTypeAAAA:return answer.IP.String()case layers.DNSTypeCNAME:return string(answer.CNAME)case layers.DNSTypeMX:return fmt.Sprintf("%s (pref %d)", string(answer.MX), answer.Preference)case layers.DNSTypeNS:return string(answer.NS)case layers.DNSTypeTXT:return string(answer.TXT)case layers.DNSTypeSOA:return fmt.Sprintf("MName: %s, RName: %s", string(answer.SOA.MName), string(answer.SOA.RName))default:return fmt.Sprintf("[Unhandled Type %d]", answer.Type)}
}

5. 处理TCP DNS流量

DNS通常使用UDP,但大响应可能使用TCP:

// 在分析函数中添加TCP处理
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {tcp, _ := tcpLayer.(*layers.TCP)if tcp.SYN {// TCP握手开始} else if len(tcp.Payload) > 0 {// 尝试解析TCP负载中的DNSdns := &layers.DNS{}err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)if err == nil {analyzeDNS(dns)}}
}

6. 完整示例:DNS监控工具

package mainimport ("fmt""log""time""github.com/google/gopacket""github.com/google/gopacket/layers""github.com/google/gopacket/pcap"
)func main() {// 1. 获取网络设备devices, err := pcap.FindAllDevs()if err != nil {log.Fatal(err)}// 打印可用设备fmt.Println("Available devices:")for _, dev := range devices {fmt.Printf("- %s", dev.Name)if dev.Description != "" {fmt.Printf(" (%s)", dev.Description)}fmt.Println()}// 2. 打开设备var deviceName = "eth0" // 根据实际情况修改var snapshotLen int32 = 1024var promiscuous = truevar timeout = 30 * time.Secondhandle, err := pcap.OpenLive(deviceName, snapshotLen, promiscuous, timeout)if err != nil {log.Fatal(err)}defer handle.Close()// 3. 设置过滤器var filter = "udp port 53 or tcp port 53"err = handle.SetBPFFilter(filter)if err != nil {log.Fatal(err)}// 4. 处理数据包packetSource := gopacket.NewPacketSource(handle, handle.LinkType())for packet := range packetSource.Packets() {processPacket(packet)}
}func processPacket(packet gopacket.Packet) {// 检查DNS层dnsLayer := packet.Layer(layers.LayerTypeDNS)if dnsLayer != nil {dns, _ := dnsLayer.(*layers.DNS)printDNSInfo(dns)}// 检查TCP DNStcpLayer := packet.Layer(layers.LayerTypeTCP)if tcpLayer != nil {tcp, _ := tcpLayer.(*layers.TCP)if len(tcp.Payload) > 0 && (tcp.SrcPort == 53 || tcp.DstPort == 53) {dns := &layers.DNS{}err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)if err == nil {printDNSInfo(dns)}}}
}func printDNSInfo(dns *layers.DNS) {// 打印基本信息direction := "Query"if dns.QR {direction = "Response"}fmt.Printf("\n=== %s ===\n", direction)fmt.Printf("Transaction ID: 0x%x\n", dns.ID)fmt.Printf("Flags: %s (QR: %t, OpCode: %s, AA: %t, TC: %t, RD: %t, RA: %t, Z: %d, RCode: %s)\n",dns.Flags, dns.QR, dns.OpCode, dns.AA, dns.TC, dns.RD, dns.RA, dns.Z, dns.ResponseCode)// 打印问题部分if len(dns.Questions) > 0 {fmt.Println("\nQuestions:")for _, q := range dns.Questions {fmt.Printf("- %s (Type: %s, Class: %s)\n", string(q.Name), q.Type, q.Class)}}// 打印回答部分if len(dns.Answers) > 0 {fmt.Println("\nAnswers:")for _, a := range dns.Answers {fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)}}// 打印权威部分if len(dns.Authorities) > 0 {fmt.Println("\nAuthorities:")for _, a := range dns.Authorities {fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)}}// 打印附加部分if len(dns.Additionals) > 0 {fmt.Println("\nAdditionals:")for _, a := range dns.Additionals {fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)}}
}func getDNSRecordValue(r layers.DNSResourceRecord) string {switch r.Type {case layers.DNSTypeA:return r.IP.String()case layers.DNSTypeAAAA:return r.IP.String()case layers.DNSTypeCNAME:return string(r.CNAME)case layers.DNSTypeMX:return fmt.Sprintf("%s (pref %d)", string(r.MX), r.Preference)case layers.DNSTypeNS:return string(r.NS)case layers.DNSTypePTR:return string(r.PTR)case layers.DNSTypeSOA:return fmt.Sprintf("MName: %s, RName: %s", string(r.SOA.MName), string(r.SOA.RName))case layers.DNSTypeTXT:return string(r.TXT)case layers.DNSTypeSRV:return fmt.Sprintf("Target: %s, Port: %d, Priority: %d, Weight: %d", string(r.SRV.Name), r.SRV.Port, r.SRV.Priority, r.SRV.Weight)default:return fmt.Sprintf("[Type %d Data]", r.Type)}
}

注意事项

  1. 权限要求:需要root或管理员权限才能捕获网络数据包
  2. 性能考虑:高流量环境下可能需要优化处理逻辑
  3. DNS over HTTPS/TLS:这种方法无法解析DoH/DoT加密的DNS流量
  4. EDNS扩展:如果需要解析EDNS扩展信息,需要额外处理OPT记录

通过这种组合使用 pcaplayers 包,你可以构建功能强大的DNS监控和分析工具,能够详细解析DNS协议的各种细节。



layers.DNS 中获取域名和对应 IP 的方法

要从 layers.DNS 数据包中提取域名和对应的 IP 地址,你需要检查 DNS 响应中的回答部分 (Answers)。以下是详细方法和示例代码:

基本方法

  1. 检查 DNS 响应:确保是 DNS 响应 (QR == true)
  2. 遍历回答记录:检查 Answers 切片
  3. 过滤 A 和 AAAA 记录:获取 IPv4 和 IPv6 地址
  4. 解析记录数据:将二进制数据转换为可读格式

示例代码

package mainimport ("fmt""github.com/google/gopacket""github.com/google/gopacket/layers""github.com/google/gopacket/pcap""net"
)func main() {// 打开网络接口handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)if err != nil {panic(err)}defer handle.Close()// 设置过滤器只捕获 DNS 响应err = handle.SetBPFFilter("udp and port 53")if err != nil {panic(err)}// 开始捕获数据包packetSource := gopacket.NewPacketSource(handle, handle.LinkType())for packet := range packetSource.Packets() {// 检查是否包含 DNS 层dnsLayer := packet.Layer(layers.LayerTypeDNS)if dnsLayer == nil {continue}dns, _ := dnsLayer.(*layers.DNS)// 只处理响应包if !dns.QR {continue}// 遍历所有回答记录for _, answer := range dns.Answers {var ip string// 根据记录类型处理switch answer.Type {case layers.DNSTypeA:// IPv4 地址if len(answer.Data) == net.IPv4len {ip = net.IP(answer.Data).String()}case layers.DNSTypeAAAA:// IPv6 地址if len(answer.Data) == net.IPv6len {ip = net.IP(answer.Data).String()}default:continue // 跳过非IP记录}if ip != "" {fmt.Printf("域名: %s, IP: %s, TTL: %d\n", string(answer.Name), ip, answer.TTL)}}}
}

更完整的处理函数

下面是一个更完整的函数,可以处理各种情况:

func extractDomainsAndIPs(dns *layers.DNS) map[string][]string {result := make(map[string][]string)// 首先收集所有查询的域名var domains []stringfor _, q := range dns.Questions {domains = append(domains, string(q.Name))}// 处理回答记录for _, answer := range dns.Answers {domain := string(answer.Name)var ip stringswitch answer.Type {case layers.DNSTypeA:if len(answer.Data) >= net.IPv4len {ip = net.IP(answer.Data).String()}case layers.DNSTypeAAAA:if len(answer.Data) >= net.IPv6len {ip = net.IP(answer.Data).String()}case layers.DNSTypeCNAME:// 处理CNAME记录cname := string(answer.Data)result[domain] = append(result[domain], "CNAME: "+cname)continuedefault:continue}if ip != "" {result[domain] = append(result[domain], ip)}}return result
}

处理特殊情况

  1. CNAME 记录:如果遇到 CNAME 记录,你可能需要继续查找对应的 A/AAAA 记录
  2. 多IP情况:一个域名可能对应多个IP地址
  3. 压缩域名layers.DNS 已经自动处理了DNS名称压缩

性能优化建议

  1. 预分配切片:如果你知道大致数量,可以预分配切片大小
  2. 重用缓冲区:在处理大量数据包时重用缓冲区
  3. 并行处理:使用 goroutine 池处理数据包

完整示例(带CNAME处理)

func processDNSResponse(dns *layers.DNS) {if !dns.QR {return // 不是响应包}// 创建域名到IP的映射domainToIPs := make(map[string][]string)cnameMap := make(map[string]string)// 首先处理CNAME记录for _, answer := range dns.Answers {if answer.Type == layers.DNSTypeCNAME {domain := string(answer.Name)cname := string(answer.Data)cnameMap[domain] = cname}}// 然后处理A和AAAA记录for _, answer := range dns.Answers {var ip stringdomain := string(answer.Name)switch answer.Type {case layers.DNSTypeA:if len(answer.Data) >= net.IPv4len {ip = net.IP(answer.Data).String()}case layers.DNSTypeAAAA:if len(answer.Data) >= net.IPv6len {ip = net.IP(answer.Data).String()}default:continue}if ip != "" {// 检查是否有CNAME链finalDomain := domainfor {if cname, exists := cnameMap[finalDomain]; exists {finalDomain = cname} else {break}}domainToIPs[finalDomain] = append(domainToIPs[finalDomain], ip)}}// 打印结果for domain, ips := range domainToIPs {fmt.Printf("域名: %s\n", domain)for _, ip := range ips {fmt.Printf("  IP: %s\n", ip)}}
}

通过这些方法,你可以有效地从 layers.DNS 数据包中提取域名和对应的IP地址,并处理各种DNS记录类型和特殊情况。

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

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

相关文章

RBTree的模拟实现

1&#xff1a;红黑树的概念 红⿊树是⼀棵⼆叉搜索树&#xff0c;他的每个结点增加⼀个存储位来表⽰结点的颜⾊&#xff0c;可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束&#xff0c;红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍&#xff0c;因…

React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例

React Router 中的 unstable_usePrompt 是一个用于在用户尝试离开当前页面时触发确认提示的自定义钩子&#xff0c;常用于防止用户误操作导致数据丢失&#xff08;例如未保存的表单&#xff09;。 一、unstable_usePrompt用途 防止意外离开页面&#xff1a;当用户在当前页面有…

OSI 7层模型

OSI 7层模型&#xff1a; 1、物理层&#xff08;光纤等把电脑连接起来的物理手段&#xff09; 2、数据链路层&#xff08;以太网&#xff0c;确认0和1电信号的分组方式&#xff0c;负责MAC地址&#xff0c;MAC地址用于在网络中唯一标示一个网卡&#xff0c;相当于网卡的身份证…

视频编解码学习十一之视频原始数据

一、视频未编码前的原始数据是怎样的&#xff1f; 视频在未编码前的原始数据被称为 原始视频数据&#xff08;Raw Video Data&#xff09;&#xff0c;主要是按照帧&#xff08;Frame&#xff09;来组织的图像序列。每一帧本质上就是一张图片&#xff0c;通常采用某种颜色格式…

Redis学习打卡-Day1-SpringDataRedis、有状态无状态

Redis的Java客户端 Jedis 以 Redis 命令作为方法名称&#xff0c;学习成本低&#xff0c;简单实用。Jedis 是线程不安全的&#xff0c;并且频繁的创建和销毁连接会有性能损耗&#xff0c;因此推荐使用 Jedis 连接池代替Jedis的直连方式。 lettuce Lettuce是基于Netty实现的&am…

告别静态配置!Spring Boo动态线程池实战指南:Nacos+Prometheus全链路监控

一、引言 1.1 动态线程池的必要性 传统线程池的参数&#xff08;如核心线程数、队列容量&#xff09;通常通过配置文件静态定义&#xff0c;无法根据业务负载动态调整。例如&#xff0c;在电商大促场景中&#xff0c;流量可能瞬间激增&#xff0c;静态线程池容易因配置不合理导…

Flask如何读取配置信息

目录 一、使用 app.config 读取配置 二、设置配置的几种方式 1. 直接设置 2. 从 Python 文件加载 3. 从环境变量加载 4. 从字典加载 5. 从 .env 文件加载&#xff08;推荐开发环境用&#xff09; 三、读取配置值 四、最佳实践建议 在 Flask 中读取配置信息有几种常见方…

【React中useCallback钩子详解】

useCallback 是 React 中的一个性能优化 Hook,用于缓存函数引用,避免在组件重新渲染时重复创建相同的函数,从而减少不必要的子组件渲染或副作用执行。以下是其核心要点: 1. 核心作用 函数记忆化:返回一个记忆化的回调函数,仅在依赖项变化时重新创建函数,否则复用之前的函…

【!!!!终极 Java 中间件实战课:从 0 到 1 构建亿级流量电商系统全链路解决方案!!!!保姆级教程---超细】

终极 Java 中间件实战课:电商系统架构实战教程 电商系统架构实战教程1. 系统架构设计1.1 系统模块划分1.2 技术选型2. 环境搭建2.1 开发环境准备2.2 基础设施部署3. 用户服务开发3.1 创建Maven项目3.2 创建用户服务模块3.3 配置文件3.4 实体类与数据库设计3.5 DAO层实现3.6 Se…

C#异步Task,await,async和Unity同步协程

标题 TaskawaitasyncUnity协程 Task Task是声明异步任务的必要关键字&#xff0c;也可以使用Task<>泛型来定义Task的返回值。 await await是用于等待一个Task结束&#xff0c;否则让出该线程控制权&#xff0c;让步给其他线程&#xff0c;直到该Task结束才往下运行。 …

【USRP】在linux下安装python API调用

UHD 源码安装 安装库 sudo apt-get install autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool \ g git inetutils-tools libboost-all-dev libncurses5 libncurses5-dev libusb-1.0-0 libusb-1.0-0-dev \ libusb-dev python3-dev python3-mako …

什么是 NoSQL 数据库?它与关系型数据库 (RDBMS) 的主要区别是什么?

我们来详细分析一下 NoSQL 数据库与关系型数据库 (RDBMS) 的主要区别。 什么是 NoSQL 数据库&#xff1f; NoSQL (通常指 “Not Only SQL” 而不仅仅是 “No SQL”) 是一类数据库管理系统的总称。它们的设计目标是解决传统关系型数据库 (RDBMS) 在某些场景下的局限性&#xf…

蓝桥杯题库经典题型

1、数列排序&#xff08;数组 排序&#xff09; 问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 输入格式 第一行为一个整数n。 第二行包含n个整数&#xff0c;为待排序的数&#xff0c;每个整数的绝对值小于10000。 输出格式 输出…

wordpress自学笔记 第三节 独立站产品和类目的三种展示方式

wordpress自学笔记 摘自 超详细WordPress搭建独立站商城教程-第三节 独立站产品和类目的三种展示方式&#xff0c;2025 WordPress搭建独立站教程#WordPress建站教程https://www.bilibili.com/video/BV1rwcteuETZ?spm_id_from333.788.videopod.sections&vd_sourcea0af3b…

智能手表蓝牙 GATT 通讯协议文档

以下是一份适用于智能手表的 蓝牙 GATT 通讯协议文档&#xff0c;适用于 BLE 5.0 及以上标准&#xff0c;兼容 iOS / Android 平台&#xff1a; 智能手表蓝牙 GATT 通讯协议文档 文档版本&#xff1a;V1.0 编写日期&#xff1a;2025年xx月xx日 产品型号&#xff1a;Aurora Wat…

Linux PCI 驱动开发指南

注&#xff1a;本文为 “Linux PCI Drivers” 相关文章合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;略作重排。 如有内容异常&#xff0c;请看原文。 How To Write Linux PCI Drivers 翻译: 司延腾 Yanteng Si siyantengloongson.cn 1. 如何写 Linux PCI 驱动 …

Python 接入DeepSeek

不知不觉DeepSeek已经火了半年左右&#xff0c;冲浪都赶不上时代了。 今天开始学习。 本文旨在使用Python调用DeepSeek的接口&#xff08; 这里写目录标题 一、环境准备1.1 DeepSeek1.2 Python 二、接入DeepSeek2.1 参数2.2 requests2.3 openai2.4 返回示例 一、环境准备 1.1…

Java 集合与 MyBatis 动态 SQL 实战教程

一、Java 集合的创建与用法 在 Java 中&#xff0c;List、HashSet 和数组是常用的集合类型&#xff0c;以下是它们的创建与基本操作&#xff1a; 1. List 列表 创建方式&#xff1a; List<Integer> list new ArrayList<>(Arrays.asList(1, 2, 3)); // 可变列…

无人机避障——(运动规划部分)深蓝学院动力学kinodynamic A* 3D算法理论解读(附C++代码)

开源代码链接&#xff1a;GitHub - Perishell/motion-planning 效果展示&#xff1a; ROS 节点展示全局规划和轨迹生成部分&#xff1a; Kinodynamic A*代码主体&#xff1a; int KinoAstar::search(Eigen::Vector3d start_pt, Eigen::Vector3d start_vel,Eigen::Vector3d en…

Transformer Decoder-Only 算力FLOPs估计

FLOPs和FLOPS的区别 FLOPs &#xff08;Floating Point Operations&#xff09;是指模型或算法执行过程中总的浮点运算次数&#xff0c;单位是“次”FLOPS &#xff08;Floating Point Operations Per Second&#xff09;是指硬件设备&#xff08;如 GPU 或 CPU&#xff09;每…