【P2P】【Go】采用go语言实现udp hole punching 打洞 传输速度测试 ping测试
服务器端 udpserver/main.go
package mainimport ("fmt""net""sync""sync/atomic"
)var (clientCounter uint64 = 0 // 客户端连接计数器mu sync.Mutex
)func main() {addr, err := net.ResolveUDPAddr("udp", ":3478")if err != nil {fmt.Println("Error resolving UDP address:", err)return}conn, err := net.ListenUDP("udp", addr)if err != nil {fmt.Println("Error listening on UDP port:", err)return}defer conn.Close()fmt.Println("UDP server is running on :3478")clientMap := make(map[string]int) // 存储客户端地址和ID的映射for {buffer := make([]byte, 1024)n, remoteAddr, err := conn.ReadFromUDP(buffer)if err != nil {fmt.Println("Error reading from UDP:", err)continue}clientKey := remoteAddr.String()fmt.Printf("Received from client %s: %s\n", clientKey, string(buffer[:n]))mu.Lock()clientID, exists := clientMap[clientKey]if !exists {clientID = int(atomic.AddUint64(&clientCounter, 1))clientMap[clientKey] = clientIDfmt.Printf("New client joined with ID %d\n", clientID)// 如果已经有两个客户端,则向新加入的客户端发送另一个客户端的信息if len(clientMap) == 2 {for key, otherID := range clientMap {if key != clientKey {otherAddr, _ := net.ResolveUDPAddr("udp", key)peerAddrStr := fmt.Sprintf("1@%s:%d", remoteAddr.IP.To4().String(), remoteAddr.Port)_, err := conn.WriteToUDP([]byte(peerAddrStr), otherAddr)if err != nil {fmt.Println("Error sending peer address to existing client:", err)} else {fmt.Printf("Sent peer address %s to existing client %d\n", peerAddrStr, otherID)}otherPeerAddrStr := fmt.Sprintf("2@%s:%d", otherAddr.IP.To4().String(), otherAddr.Port)_, err = conn.WriteToUDP([]byte(otherPeerAddrStr), remoteAddr)if err != nil {fmt.Println("Error sending peer address to new client:", err)} else {fmt.Printf("Sent peer address %s to new client %d\n", otherPeerAddrStr, clientID)}break}}// 清理clientMap,从新存入开始两个客户端for k := range clientMap {delete(clientMap, k)}}}mu.Unlock()// 直接处理已知客户端的消息if exists {fmt.Printf("Message from known client %d (%s): %s\n", clientID, clientKey, string(buffer[:n]))}}
}
客户端 udpclient/main.go
package mainimport ("flag""fmt""math/rand""net""strings""time"
)const (totalDataSize = 10 * 1024 * 1024 // 10 MBchunkSize = 50 * 1024 // 50 KBpingPacketSize = 64 // 小块UDP数据包大小,例如64字节
)var (dataChunk = make([]byte, chunkSize)pingPacket = make([]byte, pingPacketSize)
)func init() {rand.Seed(time.Now().UnixNano())for i := range dataChunk {dataChunk[i] = byte(rand.Intn(256))}for i := range pingPacket {pingPacket[i] = byte(rand.Intn(256))}
}func main() {serverAddrPtr := flag.String("server", "127.0.0.1:3478", "STUN server address (IP:port)")flag.Parse()fmt.Printf("Using STUN server address: %s\n", *serverAddrPtr)udpAddr, err := net.ResolveUDPAddr("udp", *serverAddrPtr)if err != nil {fmt.Println("Error resolving UDP address:", err)return}localAddr, err := net.ResolveUDPAddr("udp", ":0")if err != nil {fmt.Println("Error resolving local UDP address:", err)return}listener, err := net.ListenUDP("udp", localAddr)if err != nil {fmt.Println("Error listening on UDP:", err)return}defer listener.Close()fmt.Printf("Local listener created at %s\n", listener.LocalAddr())// 发送消息给 STUN 服务器获取公共地址publicAddr, err := getPublicAddr(listener, udpAddr)if err != nil {fmt.Println("Error getting public address:", err)return}fmt.Printf("Public address is %s\n", publicAddr)// publicAddr 是一个字符串,格式未id@ip:port,需要解析为 net.UDPAddr并解析出id号id := publicAddr[:strings.Index(publicAddr, "@")]publicAddr = publicAddr[strings.Index(publicAddr, "@")+1:]// 使用公共地址与对端建立直接连接peerAddr, err := establishConnection(listener, publicAddr)if err != nil {fmt.Println("Error establishing connection:", err)return}// 根据连接到服务器的顺序确定客户端和服务端角色, id ==1 则先做服务器isServer := id == "1"if isServer {runServer(listener, peerAddr)} else {runClient(listener, peerAddr)}// 角色互换再次运行if isServer {runClient(listener, peerAddr)} else {runServer(listener, peerAddr)}// 测试完成,开始ping功能runPingTest(listener, peerAddr)
}func getPublicAddr(listener *net.UDPConn, stunAddr *net.UDPAddr) (string, error) {buffer := make([]byte, 1024)_, err := listener.WriteToUDP([]byte("Binding Request"), stunAddr)if err != nil {return "", err}n, addr, err := listener.ReadFromUDP(buffer)if err != nil || addr.String() != stunAddr.String() {return "", fmt.Errorf("failed to receive response from STUN server")}// 假设 STUN 服务器返回的是公共地址字符串(实际情况需要解析 STUN 消息)publicAddrStr := string(buffer[:n])return publicAddrStr, nil
}func establishConnection(listener *net.UDPConn, publicAddr string) (*net.UDPAddr, error) {connEstablished := make(chan struct{})var peerAddr *net.UDPAddrgo func() {buffer := make([]byte, 1024)for {n, addr, err := listener.ReadFromUDP(buffer)if err != nil {fmt.Println("Error reading from peer:", err)continue}message := string(buffer[:n])fmt.Printf("Received from %s: %s\n", addr, message)if message == "Connection established" {peerAddr = addrclose(connEstablished)break}}}()// 模拟发送“Connection established”以确认连接建立time.Sleep(2 * time.Second) // 等待一段时间让对方先尝试打洞addr, err := net.ResolveUDPAddr("udp", publicAddr)if err != nil {fmt.Println("Error resolving UDP address:", err)return addr, err}listener.WriteToUDP([]byte("Connection established"), addr)<-connEstablished // 等待连接建立成return peerAddr, nil
}func runServer(listener *net.UDPConn, peerAddr *net.UDPAddr) {fmt.Println("Running as server...")buffer := make([]byte, chunkSize)receivedChunks := 0startTime := time.Now()for receivedChunks < totalDataSize/chunkSize {n, addr, err := listener.ReadFromUDP(buffer)if err != nil {fmt.Println("Error reading from peer:", err)continue}if addr.String() == peerAddr.String() && n == chunkSize {receivedChunks++fmt.Printf("Received chunk %d/%d\n", receivedChunks, totalDataSize/chunkSize)// 模拟确认收到数据listener.WriteToUDP([]byte("ACK"), peerAddr)}}duration := time.Since(startTime)speed := float64(totalDataSize) / duration.Seconds() / (1024 * 1024) // MB/sfmt.Printf("Server received all data in %.2f seconds, speed: %.2f MB/s\n", duration.Seconds(), speed)
}func runClient(listener *net.UDPConn, peerAddr *net.UDPAddr) {fmt.Println("Running as client...")sentChunks := 0startTime := time.Now()for sentChunks < totalDataSize/chunkSize {_, err := listener.WriteToUDP(dataChunk, peerAddr)if err != nil {fmt.Println("Error sending data chunk:", err)continue}sentChunks++// 等待接收端确认buffer := make([]byte, 3)n, addr, err := listener.ReadFromUDP(buffer)if err != nil || addr.String() != peerAddr.String() || string(buffer[:n]) != "ACK" {fmt.Println("Failed to receive ACK, resending chunk...")sentChunks-- // 减少计数以便重发time.Sleep(time.Second) // 等待一段时间再重发continue}fmt.Printf("Sent chunk %d/%d and received ACK\n", sentChunks, totalDataSize/chunkSize)}duration := time.Since(startTime)speed := float64(totalDataSize) / duration.Seconds() / (1024 * 1024) // MB/sfmt.Printf("Client sent all data in %.2f seconds, speed: %.2f MB/s\n", duration.Seconds(), speed)
}func runPingTest(listener *net.UDPConn, peerAddr *net.UDPAddr) {fmt.Println("Starting ping test...")const numPings = 10pingTimes := make([]time.Duration, numPings)for i := 0; i < numPings; i++ {startTime := time.Now()listener.WriteToUDP(pingPacket, peerAddr)fmt.Printf("Sent ping packet %d/%d\n", i+1, numPings)buffer := make([]byte, pingPacketSize)n, addr, err := listener.ReadFromUDP(buffer)if err != nil || addr.String() != peerAddr.String() || n != pingPacketSize {fmt.Println("Failed to receive pong, skipping this ping...")continue}elapsed := time.Since(startTime)pingTimes[i] = elapsedfmt.Printf("Received pong after %.2f ms\n", float64(elapsed)/float64(time.Millisecond))}// 计算平均延时var totalDelay time.Durationfor _, t := range pingTimes {totalDelay += t}averageDelay := totalDelay / time.Duration(numPings)fmt.Printf("Average ping delay over %d pings: %.2f ms\n", numPings, float64(averageDelay)/float64(time.Millisecond))
}
本机测试
由于数据块设置的比较小(MTU限制),且每次发送都等待了ACK,导致速度不高。后续可以考虑基于UDT协议来优化。
相关文章:
【P2P】【Go】采用go语言实现udp hole punching 打洞 传输速度测试 ping测试
服务器端 udpserver/main.go package mainimport ("fmt""net""sync""sync/atomic" )var (clientCounter uint64 0 // 客户端连接计数器mu sync.Mutex )func main() {addr, err : net.ResolveUDPAddr("udp", &q…...
【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容
背景 在尝试让 ChatGPT 自动开发一个桌面壁纸更改的功能时,发现引入了一个 wallpaper 库,这个库的入口文件是 index.js,但是 package.json 文件下的 type:"module",这样造成了无论你使用 import from 还是 require&…...
【SpringBoot 调度任务】
在 Spring Boot 中实现调度任务(Scheduled Tasks),通过使用 EnableScheduling 和 Scheduled 注解来完成。 添加依赖启用调度任务支持创建调度任务运行应用程序 添加依赖 pom.xml 文件中有以下依赖项: <dependency><gro…...
Android v4和v7冲突
android.useAndroidXtrue android.enableJetifiertruev4转成AndroidX...
【HarmonyOS之旅】HarmonyOS开发基础知识(一)
目录 1 -> 应用基础知识 1.1 -> 用户应用程序 1.2 -> 用户应用程序包结构 1.3 -> Ability 1.4 -> 库文件 1.5 -> 资源文件 1.6 -> 配置文件 1.7 -> pack.info 1.8 -> HAR 2 -> 配置文件简介 2.1 -> 配置文件的组成 3 -> 配置文…...
【排序算法】——插入排序
目录 前言 简介 基本思想 1.直接插入排序 2.希尔排序 代码实现 1.直接插入排序 2.希尔排序 总结 1.时空复杂度 2.稳定性 尾声 前言 排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列&…...
Vue todoList小项目记录
最初代码 简单搭一个vue2的小项目 App.vue <template><div id"app"><!-- 容器 --><div class"todo-container"><div class"todo-wrap"><!-- 头部 --><MyHeader :addTodo"addTodo"></…...
SQL题目笔记
一、根据需求创建表(设计合理的数据类型、长度)...
电脑开机提示error loading operating system怎么修复?
前一天电脑还能正常运行,但今天启动时却显示“Error loading operating system”(加载操作系统错误)。我已经仔细检查了硬盘、接线、内存、CPU和电源,确认这些硬件都没有问题。硬盘在其他电脑上可以正常使用,说明不是硬…...
Nginx 在不同操作系统下的安装指南
Nginx 在不同操作系统下的安装指南 一、Linux 系统下 Nginx 的安装 (一)基于 Ubuntu 系统 更新软件包列表 打开终端,首先执行sudo apt-get update命令。这一步是为了确保系统的软件包列表是最新的,能够获取到最新版本的 Nginx 及…...
景联文科技入选中国信通院发布的“人工智能数据标注产业图谱”
近日,由中国信息通信研究院、中国人工智能产业发展联盟牵头,联合中国电信集团、沈阳市数据局、保定高新区等70多家单位编制完成并发布《人工智能数据标注产业图谱》。景联文科技作为人工智能产业关键环节的代表企业,入选图谱中技术服务板块。…...
Nginx - 负载均衡及其配置(Balance)
一、概述 定义:在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载目标:最佳化资源使用、最大化吞吐率、最小化响应时间、避免过载功能:使用多台服务器提供单一服务(服务器农场&am…...
MySQL存储引擎-存储结构
Innodb存储结构 Buffer Pool(缓冲池):BP以Page页为单位,页默认大小16K,BP的底层采用链表数据结构管理Page。在InnoDB访问表记录和索引时会在Page页中缓存,以后使用可以减少磁盘IO操作,提升效率。 ○ Page根据状态可以分…...
数据资产入表 解锁智慧城市新潜力
在21世纪的科技浪潮中,智慧城市以信息技术为核心,以数据为血液,通过智能化、精细化的管理,让城市变得更加智慧、更加宜居。而数据资产入表,正是这一变革中的关键一环,它不仅推动了科技的进步,更…...
按类别调整目标检测标注框的写入顺序以优化人工审核效率
引言 在目标检测数据标注审核过程中,我们常常会遇到以下情况:某些小目标的检测框嵌套在大目标检测框内,而在模型进行预标注后,这些小目标的框可能被写入到了大目标框的下层。在人工审核阶段,标注审核人员需要手动移动…...
深入理解YOLO系列目标检测头的设定方式
目录 YOLOv1的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 YOLOv2的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 YOLOv3的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 总结:YOLO 系列检测头的结构演变 YOLOv1的检测头结构 1. 网络…...
智慧农业物联网解决方案:道品科技水肥一体化
在当今科技飞速发展的时代,农业也迎来了一场深刻的变革。智慧农业物联网解决方案中的水肥一体化技术,正逐渐成为现代农业发展的重要助推器。它不仅提高了农业生产效率,还实现了精准施肥和灌溉,为农业可持续发展带来了新的机遇。 …...
单片机上电后程序不运行怎么排查问题?
1.电源检查。使用电压表测量单片机的电源电压是否正常,确保电压在规定的范围内,如常见的5V。 2.复位检查。检查复位引脚的电压是否正常,在单片机接通电源时,复位引脚通常会有一个高电平,按下复位按钮时,复位…...
OceanBase 数据库分布式与集中式 能力
OceanBase分布式数据库与集中式数据库的差异 分布式数据库能解决金融行业最有挑战的高并发低延迟的核心交易系统的稳定性、扩展性、高性能问题。OB之所以一直强调分布式是说它具备很强的数据处理能力,当然从OB4.0开始也支持集中式了。 在实际业务场景中20%是分布式…...
C#多线程
C#中的多线程编程是开发高效并发应用程序的关键技术之一,它允许程序同时执行多个任务,从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义,我们可以从以下几个方面来探讨:线程的基本概念、创建线程的方法、…...
Apache HTTP 服务器深度性能优化
引言 在前几篇文章中,我们讨论了基础和高级性能优化策略。现在,我们将深入探讨一些具体的优化实践,帮助您实现更精细的控制,并确保Apache服务器在各种复杂环境中都能保持最佳性能。 1. 细粒度的Apache配置调整 1.1 MPM参数微调…...
芯片级IO (Pad) Ring IP Checklist
SoC top顶层数字后端实现都会涉及到IO Ring (PAD Ring)的设计。这里面包括VDD IO,VDDIO IO, Signal IO, Corner IO,Filler IO,IO power cut cell等等。 数字后端零基础入门系列 | Innovus零基础LAB学习Day2 数字IC后端实现TOP F…...
无界wujie网址
文档网址:微前端是什么 | 无界 demo:https://wujie-micro.github.io/demo-main-vue/react17...
vulnhub靶场【DriftingBlues】之6
前言 靶机:DriftingBlues-6,IP地址192.168.1.63,因为重装靶机后期为192.168.1.64 攻击:kali,IP地址192.168.1.16 都采用虚拟机,网卡为桥接模式 主机发现 使用arp-scan -l或netdiscover -r 192.168.1.1…...
心情追忆- Nginx + OpenResty 构建高可用网关
之前,我独自一人开发了一个名为“心情追忆”的小程序,旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端(小程序)开发、后端搭建到最终部署。经过一个月的努力,通过群聊分享等方式,用…...
太速科技-527-基于3U VPX XCZU15EG+TMS320C6678的信号处理板
基于3U VPX XCZU15EGTMS320C6678的信号处理板 一、板卡概述 本板卡系我司自主研发的基于3U VPX风冷、导冷架构的信号处理板,适用于高速图像处理等。芯片采用工业级设计。 板卡采用标准3U VPX架构,板上集成一片Xilinx公司ZynqUltraScale系列F…...
Vue3源码笔记阅读1——Ref响应式原理
本专栏主要用于记录自己的阅读源码的过程,希望能够加深自己学习印象,也欢迎读者可以帮忙完善。接下来每一篇都会从定义、运用两个层面来进行解析 定义 运用 例子:模板中访问ref(1) <template><div>{{str}}</div> </template> <script> impo…...
多音轨视频使用FFmpeg删除不要音轨方法
近期给孩子找宫崎骏动画,但是有很多是多音轨视频但是默认的都是日语,电视上看没办法所以只能下载后删除音轨文件只保留中文。 方法分两步,先安装FFmpeg在转文件即可。 第一步FFmpeg安装 FFmpeg是一个开源项目,包含了处理视频的…...
AtomGit 开源生态应用开发赛报名开始啦
目录 1、赛项背景2、赛项信息3、报名链接4、赛题一:开发者原创声明(DCO)应用开发赛题要求目标核心功能 5、赛题二:基于 OpenHarmony 的开源社区应用开发简介赛题要求 6、参赛作品提交初赛阶段决赛阶段 7、参赛作品提交方式 1、赛项…...
使用 NVIDIA DALI 计算视频的光流
引言 光流(Optical Flow)是计算机视觉中的一种技术,主要用于估计视频中连续帧之间的运动信息。它通过分析像素在时间维度上的移动来预测运动场,广泛应用于目标跟踪、动作识别、视频稳定等领域。 光流的计算传统上依赖 CPU 或 GP…...
C语言学习day23:WriteProcessMemory函数/游戏内存数据修改工具开发
简言: 上一章我们说了获取应用进程的某数据(data),这一章我们就说说修改内存地址的数据。想要修改内存,那么就需要我们另一个WinAPI函数:WriteProcessMemory()函数。 WriteProcessMemory()函数 函数原型…...
利用 html_table 函数轻松获取网页中的表格数据
背景/引言 在数据爬取的过程中,网页表格数据往往是研究人员和开发者的重要目标之一。无论是统计分析、商业调研还是信息整理,表格数据的结构化特性都使其具有较高的利用价值。然而,如何快速、准确地从网页中提取表格数据始终是爬虫技术的一个…...
Postman接口测试:全局变量/接口关联/加密/解密
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 全局变量和环境变量 全局变量:在postman全局生效的变量,全局唯一 环境变量:在特定环境下生效的变量,本环境内唯一 …...
手机银行模拟器,一款高仿真银行app的模拟器,可以修改姓名 卡号 余额 做转账记录 做流水
📱手机银行模拟器让你自由定制你的金融生活。无论是流水账单、金额,还是个人信息,一切都可以按照你的意愿来模拟修改,让你体验模拟器带来的快乐! 链接:https://pan.quark.cn/s/c2f614f3447f 提取码&#…...
HT7183:16V, 4.5A的DC-DC升压转换器,常用在数码相机里
HT7183描述: HT7183是一款高功率异步升压转换器,集成120mΩ功率开关管,为便携式系统提供高效的小尺寸解决方案。具有2.6V至5.5V输入电压范围,可为各类不同供电的应用提供支持。该器件具备3A开关电流能力,并且能够提供高…...
Cobalt Strike 4.8 用户指南-第十四节 Aggressor 脚本
14.1、什么是Aggressor脚本 Aggressor Script 是Cobalt Strike 3.0版及更高版本中内置的脚本语言。Aggressor 脚本允许你修改和扩展 Cobalt Strike 客户端。 历史 Aggressor Script 是 Armitage 中开源脚本引擎Cortana的精神继承者。Cortana 是通过与 DARPA 的网络快速跟踪计…...
【Qt】QWidget中的常见属性及其功能(二)
目录 六、windowOpacity 例子: 七、cursor 例子: 八、font 九、toolTip 例子: 十、focusPolicy 例子: 十一、styleSheet 计算机中的颜色表示 例子: 六、windowOpacity opacity是不透明度的意思。 用于设…...
对象的克隆 单例模式
1) 如何实现对象的克隆? 1、为什么需要实现对象的克隆? 在某些情况下,需要创建一个与现有对象完全相同的副本,这就是对象克隆。 例如,在需要对对象进行备份、在不同的上下文中使用相同的类型的对象或者实现某些设计…...
预处理内容
预处理是干什么的呢? 分为三点: 1.宏替换 2.头文件导入 3.删除注释 #ifdef #include <iostream> // 定义一个宏,表示当前处于调试模式,在实际调试时可以定义这个宏,发布时取消定义#define DEBUG MODE int ma…...
Docker笔记
1 安装docker b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcucLZbrn2d 项目的资料地址(飞书) 当使用docker pull +名字 拉取镜像时报 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for co…...
条件随机场(CRF)详解:原理、算法与实现(深入浅出)
目录 1. 引言2. 什么是条件随机场?2.1 直观理解2.2 形式化定义 3. CRF的核心要素3.1 特征函数3.2 参数学习 4. 实战案例:命名实体识别5. CRF vs HMM6. CRF的优化与改进6.1 特征选择6.2 正则化 7. 总结与展望参考资料 1. 引言 条件随机场(Conditional Ra…...
C++类与对象学习笔记(一)
https://www.bilibili.com/video/BV1jm4y1w7pa?spm_id_from333.788.player.switch&vd_sourcee8984989cddeb3ef7b7e9fd89098dbe8&p6 🚩🚩🚩来自b站“码农论坛”的视频“类与对象”做的笔记🚩🚩Ὢ…...
wrk如何测试post请求
wrk git地址 https://github.com/wg/wrk wrk 默认是针对 GET 请求的,但它也可以通过添加自定义的 HTTP 请求体和 头部信息来进行 POST 请求的压测。以下是详细的步骤: wrk -t4 -c100 -d30s -s post.lua http://example.com-t4:使用 4 个线…...
rust使用log与env_logger两个crate实现同时向控制台和文件输出日志。并在隔日自动创建新日志文件。
还是老习惯,不用太多的废话。直接上代码。 不过我之说一句话,这块需要自定义一个输出的Target来实现这个功能。 log = { version="0.4.22", default-features = false } env_logger = "0.11.5"pub(crate) fn setup_log_env(log_level: LevelFilter) {...
异步将用户信息存入 Redis 缓存
主要是为了解决Redis的缓存问题,异步将用户信息存入Redis缓存 首先我们需要引入一部线性池 核心概念 异步执行: 异步执行是指任务提交后不会立即等待其完成,而是立即返回并继续执行其他任务。任务将在后台执行,执行结果可以通过…...
WebRTC服务质量(05)- 重传机制(02) NACK判断丢包
WebRTC服务质量(01)- Qos概述 WebRTC服务质量(02)- RTP协议 WebRTC服务质量(03)- RTCP协议 WebRTC服务质量(04)- 重传机制(01) RTX NACK概述 WebRTC服务质量(…...
MySQL 存储过程与函数:增强数据库功能
一、MySQL 存储过程与函数概述 (一)存储过程的定义与特点 存储过程是一组预编译的 SQL 语句集合,它们被存储在数据库中,可根据需要被重复调用。例如,在一个电商系统中,经常需要查询某个时间段内的订单数据…...
丹摩|丹摩助力selenium实现大麦网抢票
丹摩|丹摩助力selenium实现大麦网抢票 声明:非广告,为用户体验 1.引言 在人工智能飞速发展的今天,丹摩智算平台(DAMODEL)以其卓越的AI算力服务脱颖而出,为开发者提供了一个简化AI开发流程的强…...
springcloud-gateway获取应用响应信息乱码
客户端通过springcloud gateway跳转访问tongweb上的应用,接口响应信息乱码。使用postman直接访问tongweb上的应用,响应信息显示正常。 用户gateway中自定义了实现GlobalFilter的Filter类,在该类中获取了上游应用接口的响应信息,直…...
Scala项目(一)
1,创建dao,models,service,ui等软件包 2,在各软件包下创建scala类 软件包dao里的代码 package org.app package daoimport models.BookModelimport scala.collection.mutable.ListBuffer//图书,数据操作…...