go实现简单的内网穿透

news2024/12/28 1:56:09

一、内网穿透

1. 内网穿透介绍

内网穿透是一种通过互联网建立远程连接到私有网络(内网)中设备的技术。通常情况下,内网设备由于位于一个受限制的网络环境中,无法直接从外部访问。内网穿透通过将内网设备的流量转发到公共网络上的服务器,然后再将该流量转发到客户端,从而实现与内网设备的通信。

2.内网穿透下访问流程

假设目前已经实现了内网穿透,以下从用户角度出发,给出了用户访问穿透后的公网网络的步骤

  1. 用户发出请求:用户通过公网地址发出一个访问请求,目标是服务端穿透后的公网地址。
  2. 服务端接收请求:服务端监听公网地址,接收到用户的请求后,将请求信息发送给客户端,如一个"New Connection"的消息。
  3. 客户端接收请求:客户端在内网中,持续监听来自服务端的信息。当接收到"New Connection"的消息后,知道有新的请求需要处理。
  4. 客户端连接隧道服务器:客户端接收到新的请求后,会创建一个新的连接到隧道服务器。隧道服务器在公网上,是客户端和服务端之间的桥梁。
  5. 客户端连接内网服务:同时,客户端也会创建一个新的连接到内网中的目标服务。这个服务是用户最初想要访问的服务。
  6. 数据转发:客户端将内网服务的数据拷贝到隧道服务器(如io.Copy(tunnelConn, clientConn)),同时将隧道服务器的数据拷贝到内网服务(io.Copy(clientConn, tunnelConn))。这样,用户的请求就通过隧道服务器,被转发到了内网服务。
  7. 服务端接收隧道数据:服务端在公网上,监听来自隧道服务器的连接。当隧道服务器有新的数据时,服务端会接收这些数据。
  8. 服务端转发数据:服务端接收到隧道服务器的数据后,会将这些数据转发给公网地址。可以通过io.Copy(appConn, tunnelConn)io.Copy(tunnelConn, appConn)实现,这两个函数将隧道服务器的数据拷贝到用户,同时将用户的数据拷贝到隧道服务器。
  9. 用户接收数据:用户最终接收到来自服务端的数据,这些数据实际上是内网服务的响应。这样,用户就成功地通过公网访问了内网服务。

以上就是用户发出访问请求到服务端、隧道、客户端的详细过程。通过这个过程,我们可以看到内网穿透是如何工作的。

二、go实现简单的内网穿透

实现内网穿透需要两个部分:客户端和服务端。客户端运行在内网中,服务端运行在公网中。客户端和服务端通过隧道进行通信,客户端将内网服务的数据发送到服务端,服务端将公网的请求发送到客户端。

要实现内网穿透,通常需要以下几个步骤:

  • 服务器端:在公网上搭建一个可被访问的服务器,用于接收外部连接和内网设备的转发流量,当然若没有公网ip,也可以使用本地的端口进行模拟
  • 客户端:在内网设备所在的计算机上运行一个客户端程序,负责将内网设备的流量转发到服务器。
  • 端口映射:将服务器上的某个端口与内网设备的指定端口进行映射,使得外部可以通过这个端口访问内网设备。
  • 数据传输:服务器接收到来自客户端的数据后,将其转发给内网设备,同时将内网设备的响应再转发回客户端。

0. 项目源码地址:https://github.com/Pistachiout/Net-Penetration

1. constant.go常量端口定义

package constant

const (

	// 需要内网穿透的服务端口,运行内网穿透客户端后,该端口将映射到公网,公网可以访问该端口
	AppPort = ":4000"

	// ServerPort 服务端监听端口,用于连接客户端,一般为公网端口,服务端监听该端口,接收客户端的数据,然后转发给服务端本地服务
	ServerPort = ":8081"
	// TunnelPort 隧道端口,用于客户端和服务端建立隧道,客户端和服务端通过该端口通信
	TunnelPort = ":8082"
	// AppTargetPort 穿透后的目的服务端口,在服务端端口运行客户端应用
	AppTargetPort = ":8083"

	// ServerIP 服务端公网ip地址,用于连接服务端,一般为公网IP
	ServerIP = ""
	// ServerAddr 服务端监听地址,用于接收客户端的数据,然后转发给服务端本地服务
	ServerAddr = ServerIP + ServerPort
	// TunnelAddr 隧道地址,用于客户端和服务端建立隧道,客户端和服务端通过该地址通信
	TunnelAddr = ServerIP + TunnelPort

	// BufSize 缓冲区大小
	BufSize = 1024
)

以上常量定义了在内网穿透过程中所需的各种端口和地址。

  • AppPort: 内网端口,需要内网穿透的应用端口,如web应用。当运行内网穿透客户端后,该端口将被映射到公网,使得通过公网可以访问该端口对应的服务。
  • ServerPort: 服务端监听端口。服务端监听该端口,接收客户端的数据,发给服务端本地服务。
  • TunnelPort: 隧道端口。用于客户端和服务端建立隧道通信,客户端和服务端通过该端口进行交互。
  • AppTargetPort: 穿透后的目标服务端口。在服务端端口运行客户端应用,用户可通过访问公网ip的该端口访问内网服务
  • BufSize: 缓冲区大小。定义了在数据传输过程中所使用的缓冲区大小,一般为1024字节。

这些常量提供了在内网穿透过程中所需的各种配置信息,可以根据具体的应场景和需求进行相应的调整和配置。若用户拥有公网ip,还需要添加公网ip地址

  • ServerIP: 服务端公网IP地址。用于连接服务端,一般为公网IP,用户可根据自己的公网ip进行设置。客户端需要根据这个IP来与服务端建立连接。
  • ServerAddr: 服务端监听地址。用于接收客户端的数据,然后转发给服务端本地服务。由ServerIPServerPort组合而成。
  • TunnelAddr: 隧道地址。用于客户端和服务端建立隧道通信,客户端和服务端通过该地址进行交互。由ServerIPTunnelPort组合而成。

2. 辅助函数包helper.go

package helper

import (
	"log"
	"net"
	"time"
)

// CreateListen 监听,参数为监听地址listenAddr,返回 TCPListener,通过 net.ResolveTCPAddr 解析地址,通过 net.ListenTCP 监听端口
//
//	监听是指服务端监听某个端口,等待客户端的连接,一旦客户端连接上来,服务端就会创建一个新的goroutine处理客户端的请求。
//
// ResolveTCPAddr是一个解析TCP地址的函数,addr为域名或者IP地址加端口号,返回一个TCPAddr,该结构体包含了ip和port
// ListenTCP函数监听TCP地址,addr则是一个TCP地址,返回值l是一个net.Listener接口,可以用来接收连接。
func CreateListen(listenAddr string) (*net.TCPListener, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", listenAddr)
	if err != nil {
		return nil, err
	}
	tcpListener, err := net.ListenTCP("tcp", tcpAddr)
	return tcpListener, err
}

// CreateConnect 连接,参数为服务端地址connectAddr,返回 TCPConn,通过 net.ResolveTCPAddr 解析地址,通过 net.DialTCP 连接服务端
// 连接是指客户端连接服务端,连接成功后,客户端就可以向服务端发送数据了,与监听不同的是,连接是客户端发起的,而监听是服务端发起的。
// DialTCP函数在网络协议tcp上连接本地地址laddr和远端地址raddr
func CreateConnect(connectAddr string) (*net.TCPConn, error) {
	// 解析地址,返回TCPAddr
	tcpAddr, err := net.ResolveTCPAddr("tcp", connectAddr)
	if err != nil {
		return nil, err
	}
	tcpConn, err := net.DialTCP("tcp", nil, tcpAddr)
	return tcpConn, err
}

// KeepAlive 保持连接,参数为连接conn,通过循环向连接中写入数据,保持连接,每隔3秒写入一次,如果写入失败,说明连接已经断开,退出循环
func KeepAlive(conn *net.TCPConn) {
	for {
		_, err := conn.Write([]byte("KeepAlive"))
		if err != nil {
			log.Printf("[KeepAlive] Error %s", err)
			return
		}
		time.Sleep(time.Second * 3)
	}
}

// GetDataFromConnection for循环获取Connection中的数据
func GetDataFromConnection(bufSize int, conn *net.TCPConn) ([]byte, error) {
	b := make([]byte, 0)
	for {
		// 读取数据
		data := make([]byte, bufSize)
		n, err := conn.Read(data)
		if err != nil {
			return nil, err
		}
		b = append(b, data[:n]...)
		if n < bufSize {
			break
		}
	}
	return b, nil
}

这段代码是项目中常用的辅助函数包,它提供了一些用于网络通信的帮助函数:

  1. CreateListen(listenAddr string) (*net.TCPListener, error):该函数用于创建一个TCP监听器。它接受一个监听地址listenAddr作为参数,并通过net.ResolveTCPAddr解析地址,然后通过net.ListenTCP监听指定的端口。函数返回一个*net.TCPListener类型的指针和一个错误。
  2. CreateConnect(connectAddr string) (*net.TCPConn, error):该函数用于创建一个TCP连接。它接受一个服务端地址connectAddr作为参数,并通过net.ResolveTCPAddr解析地址,然后通过net.DialTCP与服务端建立连接。函数返回一个*net.TCPConn类型的指针和一个错误
  3. KeepAlive(conn *net.TCPConn):该函数用于保持TCP连接的活跃状态。通过循环向连接中写入数据,保持连接,每隔3秒写入一次,如果写入失败,说明连接已经断开,退出循环
  4. GetDataFromConnection(bufSize int, conn *net.TCPConn) ([]byte, error):该函数用于从TCP连接中获取数据。它接受一个缓冲区大小bufSize和一个*net.TCPConn类型的指针作为参数。函数通过循环从连接中读取数据,并将读取的数据追加到一个字节数组中,直到读取完所有数据或达到缓冲区大小。函数返回读取到的数据字节数组和一个错误。

3. 客户端main.go

package main

import (
	"Net-Penetration/constant"
	"Net-Penetration/helper"
	"io"
	"log"
)

// 内网穿透客户端,用于将本地服务映射到公网,使得公网可以访问本地服务,实现内网穿透,
func main() {
	// 连接服务端
	conn, err := helper.CreateConnect(constant.ServerAddr)
	if err != nil {
		panic(err)
	}
	log.Printf("连接成功,连接地址为:%s\n", conn.RemoteAddr().String())

	// 保持连接,读取数据
	for {
		// 从连接中读取数据
		data, err := helper.GetDataFromConnection(constant.BufSize, conn)
		if err != nil {
			log.Printf("读取数据失败,错误信息为:%s\n", err.Error())
			continue
		}
		log.Printf("接收到数据:%s\n", string(data))
		// 判断是否为新连接,如果是新连接,则连接隧道服务器,否则转发消息
		if string(data) == "New Connection" {
			// 连接隧道服务器
			go messgaeForward()
		}
	}
}

// 连接隧道服务器进行消息转发
func messgaeForward() {
	// 连接隧道服务器
	tunnelConn, err := helper.CreateConnect(constant.TunnelAddr)
	if err != nil {
		panic(err)
	}

	// 连接客户端服务
	clientConn, err := helper.CreateConnect(constant.AppPort)
	if err != nil {
		panic(err)
	}

	//	消息转发
	//	io.Copy()函数实现了数据的拷贝,可以将数据从一个接口拷贝到另一个接口,这里将客户端的数据拷贝到隧道服务器,将隧道服务器的数据拷贝到客户端
	//	io.Copy()函数会一直阻塞,直到两个接口中的数据全部拷贝完成
	go io.Copy(clientConn, tunnelConn)
	go io.Copy(tunnelConn, clientConn)
}

  1. main()函数:该函数是程序的入口点。它首先通过helper.CreateConnect函数连接到服务端,并打印连接地址。然后进入一个无限循环,从连接中读取数据。如果读取到的数据是"New Connection",则调用messgaeForward()函数连接隧道服务器进行消息转发。
  2. messgaeForward()函数:该函数用于连接隧道服务器并进行消息转发。它首先通过helper.CreateConnect函数分别连接到隧道服务器和客户端服务。然后使用io.Copy函数将客户端的数据拷贝到隧道服务器,将隧道服务器的数据拷贝到客户端。这样可以实现消息的双向转发。
    内网穿透的实现主要涉及到两个部分:客户端(Client)和服务端(Server)。下面我们将从代码角度出发,详细解释这两部分如何实现内网穿透。

客户端的主要任务是连接到服务端,并将内网服务的数据发送到服务端。这是通过以下关键步骤实现的:

  1. 连接到服务端:客户端首先需要连接到服务端。这是通过helper.CreateConnect(constant.ServerAddr)函数实现的,该函数创建一个到服务端的TCP连接。
  2. 读取服务端数据:客户端连接到服务端后,需要读取服务端发送的数据。这是通过helper.ReadMessage(serverConn)函数实现的,该函数从服务端连接中读取数据。
  3. 处理服务端数据:客户端读取到服务端数据后,需要根据数据内容进行相应的处理。如果读取到的数据是"New Connection",则需要连接到隧道服务器进行消息转发。
  4. 消息转发:消息转发是通过messgaeForward()函数实现的。该函数首先连接到隧道服务器和客户端服务,然后将客户端服务的数据拷贝到隧道服务器,同时将隧道服务器的数据拷贝到客户端服务。

4.服务端main.go

package main

import (
	"Net-Penetration/constant"
	"Net-Penetration/helper"
	"io"
	"log"
	"net"
	"sync"
)

// serverConn 服务端连接
var serverConn *net.TCPConn

// appConn 目的服务端连接
var appConn *net.TCPConn

// wg 用于等待所有协程结束
var wg sync.WaitGroup

// 内网穿透服务端
func main() {
	//监听服务端
	go serverListen()
	//监听目的服务端
	go appListen()
	//启动隧道服务
	go tunnelListen()

	//等待所有协程结束
	wg.Add(1)
	wg.Wait()
}

func serverListen() {
	//监听服务端,用于接收客户端连接
	tcpListener, err := helper.CreateListen(constant.ServerAddr)
	if err != nil {
		panic(err)
	}
	log.Printf("服务端监听地址为:%s\n", tcpListener.Addr().String())

	//接收客户端连接
	for {
		serverConn, err = tcpListener.AcceptTCP()
		if err != nil {
			log.Printf("接收连接失败,错误信息为:%s\n", err.Error())
			return
		}
		//保持连接
		go helper.KeepAlive(serverConn)
	}
}

// 监听隧道服务,用于接收隧道客户端连接,隧道客户端连接用于转发目的服务端和客户端之间的消息,实现内网穿透
func tunnelListen() {
	tcpListener, err := helper.CreateListen(constant.TunnelAddr)
	if err != nil {
		panic(err)
	}
	log.Printf("隧道监听地址为:%s\n", tcpListener.Addr().String())
	for {
		tunnelConn, err := tcpListener.AcceptTCP()
		if err != nil {
			log.Printf("接收连接失败,错误信息为:%s\n", err.Error())
			return
		}
		// 数据转发
		go io.Copy(appConn, tunnelConn)
		go io.Copy(tunnelConn, appConn)
	}
}

// 监听目的服务端,用于接收目的服务端连接,目的服务端可以是本地转发的端口,也可以是远程服务器的端口
func appListen() {
	//监听目的服务端
	tcpListener, err := helper.CreateListen(constant.AppTargetPort)
	if err != nil {
		panic(err)
	}
	log.Printf("应用目的服务端监端口地址为:%s\n", tcpListener.Addr().String())

	for {
		appConn, err = tcpListener.AcceptTCP()
		if err != nil {
			log.Printf("接收连接失败,错误信息为:%s\n", err.Error())
			return
		}
		_, err := serverConn.Write([]byte("New Connection"))
		if err != nil {
			log.Printf("发送消息失败,错误信息为:%s\n", err.Error())
		}
	}
}

  1. main()函数:该函数是程序的入口点。它启动了三个协程,分别用于监听服务端、目的服务端和隧道服务端。然后使用sync.WaitGroup等待所有协程结束。
  2. serverListen()函数:该函数用于监听客户端连接。它首先通过helper.CreateListen函数创建一个TCP监听器,并打印监听地址。然后进入一个无限循环,接收客户端连接。每当有新的客户端连接时,将其保持活跃,并启动一个新的协程进行处理。
  3. appListen()函数:该函数用于监听目的服务端连接。它首先通过helper.CreateListen函数创建一个TCP监听器,并打印监听地址。然后进入一个无限循环,接收目的服务端连接。每当有新的目的服务端连接时,向客户端发送"New Connection"的消息。
  4. tunnelListen()函数:该函数用于监听隧道客户端连接。它首先通过helper.CreateListen函数创建一个TCP监听器,并打印监听地址。然后进入一个无限循环,接收隧道客户端连接。每当有新的隧道客户端连接时,启动两个新的协程,分别将数据从隧道客户端拷贝到目的服务端,以及将数据从目的服务端拷贝到隧道客户端。

服务端的主要任务是监听客户端连接、目的服务端连接和隧道客户端连接,并进行数据转发。这是通过以下关键步骤实现的:

  1. 监听客户端连接:服务端首先需要监听客户端连接。这是通过helper.CreateListen(constant.ServerListenAddr)函数实现的,该函数创建一个监听客户端连接的TCP监听器。
  2. 处理客户端连接:服务端接收到客户端连接后,需要保持连接。这是通过helper.KeepAlive(serverConn)函数实现的,该函数保持与客户端的连接。
  3. 监听目的服务端连接:服务端需要监听目的服务端连接。这是通过helper.CreateListen(constant.AppTargetPort)函数实现的,该函数创建一个监听目的服务端连接的TCP监听器。
  4. 处理目的服务端连接:服务端接收到目的服务端连接后,需要向客户端发送"New Connection"的消息。这是通过serverConn.Write([]byte("New Connection"))实现的。
  5. 监听隧道客户端连接:服务端需要监听隧道客户端连接。这是通过helper.CreateListen(constant.TunnelListenAddr)函数实现的,该函数创建一个监听隧道客户端连接的TCP监听器。
  6. 数据转发:服务端接收到隧道客户端连接后,需要进行数据转发。这是通过io.Copy(appConn, tunnelConn)io.Copy(tunnelConn, appConn)实现的,这两个函数将隧道客户端的数据拷贝到目的服务端,同时将目的服务端的数据拷贝到隧道客户端。

5.应用端测试main.go

package main

import (
	"Net-Penetration/constant"
	"encoding/json"
	"log"
	"net/http"
)

// 本地应用,用于测试内网穿透
func main() {
	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		q := request.URL.Query()
		b, err := json.Marshal(q)
		if err != nil {
			log.Println(err)
		}
		writer.Write(b)
	})
	log.Printf("本地服务已启动:%s\n", constant.AppPort)
	http.ListenAndServe(constant.AppPort, nil)
}

6.1 本地测试步骤:

1.首先运行服务端main.go
2. 运行客户端main.go
3. 运行应用端main.go
4. 此时可分别打开AppPort和AppTargetPort均可发现应用服务

在这里插入图片描述

6.2 公网ip测试步骤:以Linux云服务器为例

  1. 首先修改constant.go中的ServerIP为自己的公网ip
  2. 将服务端main.go打包,上传到Linux云服务器,并添加权限。windows下将go程序打包为linux可执行程序需要配置go编译环境,具体参考windows下将go程序打包为linux可执行程序教程:
  3. 在服务器运行上传的main
  4. 在客户端运行客户端main.go
  5. 运行应用端main.go
  6. 此时可打开公网ip:AppTargetPort可发现内网的应用服务

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

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

相关文章

自动化测试框架[各自动化测试框架大比拼]

目录 Cypress与Selenium/WebDriver Language Bindings/Client Selenium/WebDriver执行慢 Cypress与Selenium/WebDriver比较 Cypress与Karma比较 Cypress与Nightwatch比较 Cypress与Protractor比较 Cypress与TestCafe比较 Cypress和Puppeteer比较 Cypress与Selenium/We…

浅谈智能电容器的结构与功能在电网中的应用 安科瑞 许敏

摘要&#xff1a;近年来&#xff0c;随着我国电力工业的不断发展&#xff0c;大范围的高压输电网络逐渐形成&#xff0c;同时对电网无功功率的要求也日渐严格。电网无功功率不平衡将导致系统电压的巨大波动&#xff0c;严重时会导致用电设备的损坏。因此&#xff0c;研究无功补…

C++与QML 信号与槽交互

一、全局属性 在main.cpp中通过 QQmlApplicationEngine engine 获得全局对象 QQmlApplicationEngine engine; //全局对象 上下文对象 QQmlContext* context engine.rootContext(); //获取屏幕的大小 Screen *screen QGuiApplication::primaryScreen(); …

jquery基础知识整理

写在前面的话&#xff1a;由于公司做的政府网站&#xff0c;需要考虑到兼容ie。所以我们使用jquery解决方案。在一些展示类的、不是一个多网页的整体应用中。我们仍然可以使用jQuery进行开发。 以下是我看慕课网整理的jQuery知识。慕课网课程链接jQuery课程。 以下是我整理的知…

文档识别开发“零门槛”!合合信息智能文字识别技术助力柳州银行“降本增效”

现阶段&#xff0c;越来越多的金融机构将业务转到线上&#xff0c;推出了“零接触”金融服务以提升业务流程效率。在此过程中&#xff0c;智能文字识别技术对提升复杂版式文档录入效率起到了重要作用。 近期&#xff0c;在元脑生态框架下&#xff0c;合合信息将智能文字识别技…

数学建模算法(基于matlab和python)之 三次样条插值与多项式拟合(2/10)

实验目的及要求&#xff1a; 1、会在Matlab平台上进行样条插值&#xff1b; 2、会在Matlab平台上进行最小二乘拟合。 实验内容&#xff1a; 1、利用spline函数绘制【0&#xff0c;10】内步长为1的插值节点处的的样条曲线。 2、根据下列数据三转角方程法构造三次样条函数…

Linux命令大全(面试必备)

前两节有说Git命令&#xff0c;反馈还不错&#xff0c;看来大家对这些必备的命令还挺感兴趣哈&#xff0c;这节就罗列一些Linux必须掌握的命令。 干货满满哦&#xff0c;直接发车... 一、常用的基本命令 1、关机开机 关机 shutdown-h now 立刻关机shutdown-h 3 3分钟后关机…

Java八股文面试(黑马)

Redis篇 数据库的并发不高&#xff0c;请求到了一定的量就会击垮数据库&#xff0c;导致宕机。 大概率是客户端正好在写入数据&#xff0c;sentinel与当前master出现分区 而 重新选举了一个master Mysql篇

IOS 手机抓包 频繁 访问 gateway.icloud.com.cn:443

在抓包的时候&#xff0c;有时候苹果手机在重启后频繁出现gateway.icloud.com.cn:443请求 这种情况需要在手机上进行设置&#xff0c;适用于IOS16.0以上版本&#xff0c;其他版本不清楚&#xff0c;没有尝试过 进入 设置--点击AppleID &#xff0c;进入个人信息页面---点击iCl…

开发工具-网络测试工具 POSTMAN 的脚本使用

开发工具-网络测试工具 POSTMAN 的脚本使用 postman 脚本应用基础设置变量、获取变量示例环境变量的使用测试 发送请求示例常用的变量、方法responseBody 获取响应体内容pm.response 获取响应信息pm.expect() JSON检查cheerio.load() 类似于 jquery 操作 html 元素 POSTMAN 是网…

做独立站要选择什么样的产品呢?这些要点要注意!

跨境圈都在说独立站能不能成功七分靠选品&#xff0c;三分靠运营&#xff0c;可见选品是决定独立站能不能成功的一大关键因素&#xff0c;只有先选定了产品&#xff0c;才能进行店铺装修、物流设置&#xff0c;以及后续的运营工作。 那么独立站的卖家应该如何去选品呢&#xf…

一文搞懂ASCII、Unicode与UTF-8

目录 一、前置知识二、ASCII三、从ASCII到Unicode四、Unicode五、UTF-8六、总结References 一、前置知识 一个字节有8-bit&#xff1b;一个十六进制数占4-bit&#xff0c;故一个字节可以表示为两个十六进制数&#xff08;通常会加上 0x 前缀&#xff09;&#xff1b;十六进制通…

基于Python+OpenCV的视频字符化(深度学习+机器视觉)含全部工程源码

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境OpenCV环境 模块实现1. 视频读取及处理2. 色素块识别与替换3. 视频合成4. 操作系统上的实现 系统测试工程源代码下载其它资料下载 前言 本项目利用 OpenCV 提供的轻量、高效的 C类和 Python 接口&#xff0c;实…

数据平台流量回放最佳实践|精选

1.1 数据平台业务背景 数据平台利用大数据智能分析、数据可视化等技术&#xff0c;对公司内外部经过采集、建设、管理、分析的多源异构数据进行呈现和应用&#xff0c;实现了数据共享、日常报表自动生成、快速和智能分析&#xff0c;深度挖掘数据价值&#xff0c;满足企业各级…

Leetcode48 旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出…

ZYNQ——按键消抖实验

文章目录 原理简介软件仿真板上验证 原理简介 按键作为基本的人机输入接口&#xff0c;在很多电子设计中都是比较常见的&#xff0c;但是由于其机械特性&#xff0c;在按键按下或者松开的时候&#xff0c;按键的输入值是有抖动的。按键的抖动是其固有特性&#xff0c;因此无论…

打死也不要进外包,来看看我在腾讯外包的这2年......

我18年毕业于一个普通二本学校&#xff0c;是一个很不出名的小本科。大学期间专业知识也没有去认真的学习&#xff0c;所以毕业的时候就随便找了一份工作&#xff0c;在一个小公司做功能测试。 记得那时候薪资大概是6k左右&#xff0c;因为是在工厂&#xff0c;工作环境很差&a…

Gitlab 访问令牌更新 替换

引言 访问令牌到期后如何丝滑更换&#xff0c;或则如何更改账户的访问令牌实现权限变动呢&#xff1f;在我们使用访问令牌克隆项目之后&#xff0c;Windows凭据就帮我们记录下了密码。当然密码是不可见的&#xff0c;只能进行删除重新设置&#xff0c;通过凭据管理器进行更改。…

python基于预训练大模型权重实现文本向量化开发构建微博评论数据情感极向识别模型

因为最近的一些项目涉及到了大模型相关的技术开发研究&#xff0c;在我前面的博文中也已经有一些实践记录学习资料分享的文章了&#xff0c;今天是在做模型训练的时候突然想到一个有意思的想法&#xff0c;按照我之前做文本分类的方法&#xff0c;基本主流的技术路线都是先对文…

性能提升571%,32K超长上下文,推理速度提升42%,允许商用,国产开源大模型推出了二代 ChatGLM2-6B...

6月份是火热的夏天&#xff0c;各种火热的 AI 产品也在密集的更新&#xff0c;天越热&#xff0c;大家是干得热火朝天&#xff0c;卷出了新高度。 前有现在大火的 ChatGPT&#xff0c;ChatGPT 更新&#xff1a;大杀器&#xff01;函数调用示范&#xff0c;ChatGPT 3.5/4 双双升…