Go编写流量代理工具

news2024/11/24 3:28:53

目录

    • 这是一个演示
    • 主要分为俩包:
    • 流程:
    • 逻辑:(端口随意,本地ssh为例)
    • 用法:
    • 文件地址:
    • 代码如下:

这是一个演示

代理本地HTTP服务
在这里插入图片描述
代理局域网SSH服务
在这里插入图片描述
在这里插入图片描述
其他的TCP服务没测试了


主要分为俩包:

main -》 主函数,包含Server、Client函数和goroutine函数
utils -》 工具函数,包含发包和读包函数,现在使用的是json序列化没有加密(内置AES256加密,可以把粘包问题处理后进行使用)


流程:

返回数据呈现给用户:

本地Conn -》服务器中转Conn -》 服务器代理Conn

用户发送请求到本地:

服务器代理Conn -》 本地中转Conn -》 本地Conn


逻辑:(端口随意,本地ssh为例)

  • Server (对外IP 1.2.3.4)
    1. 先监听0.0.0.0:2222(中转代理服务)
      • 有连接 -》进行②
      • 无连接 -》 继续听(每隔5秒)
    2. 再监听0.0.0.0:3333(代理出来的端口)

  • Client
    1. 先连接127.0.0.1:22(本地服务)(这两个先后顺序无所谓)
      • 成功 -》进行②
      • 失败-》 继续连(每隔5秒)
    2. 再连接1.2.3.4:2222(中转代理服务)

用法:

使用方法:
服务器模式:
  程序名 -s R1地址:端口 Up地址:端口
  例子:程序名 -s x.x.x.x:2222 x.x.x.x:3333
客户端模式:
  程序名 -c 本地地址:端口 R1地址:端口
  例子:程序名 -c 127.0.0.1:22 x.x.x.x:2222
显示帮助信息:
  程序名 -h

文件地址:

Github地址:https://github.com/jumppppp/go/tree/master/htools/pangolin

代码如下:

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net"
	"pangolin/utils"
	"time"
)

type Client_t struct{
	ConnLocal net.Conn
	BufR1 [1024]byte
	ConnR1 net.Conn
	ConnectedR1 bool
}
type Server_t struct{
	LnR1 net.Listener
	ConnR1 net.Conn
	ConnectedUp bool
	LnUp net.Listener
	ConnUp net.Conn
	BufR1 [1024]byte
}

func printUsage() {
	fmt.Println("使用方法:")
	fmt.Println("服务器模式:")
	fmt.Println("  程序名 -s R1地址:端口 Up地址:端口")
	fmt.Println("  例子:程序名 -s x.x.x.x:2222 x.x.x.x:3333")
	fmt.Println("客户端模式:")
	fmt.Println("  程序名 -c 本地地址:端口 R1地址:端口")
	fmt.Println("  例子:程序名 -c 127.0.0.1:22 x.x.x.x:2222")
	fmt.Println("显示帮助信息:")
	fmt.Println("  程序名 -h")
}
func main() {
	var (
		helpFlag   bool
		serverFlag bool
		clientFlag bool
	)

	// 设置命令行参数
	flag.BoolVar(&helpFlag, "h", false, "显示帮助信息")
	flag.BoolVar(&serverFlag, "s", false, "使用服务器模式")
	flag.BoolVar(&clientFlag, "c", false, "使用客户端模式")

	flag.Parse()

	if helpFlag {
		printUsage()
		return
	}

	args := flag.Args()

	if serverFlag {
		if len(args) != 2 {
			fmt.Println("错误:请提供R1地址和Up地址")
			printUsage()
			return
		}

		HpR1 := args[0]
		HpUp := args[1]

		Server(HpR1, HpUp)
	} else if clientFlag {
		if len(args) != 2 {
			fmt.Println("错误:请提供本地地址和R1地址")
			printUsage()
			return
		}

		HpLocal := args[0]
		HpR1 := args[1]

		Client(HpR1, HpLocal)
	} else {
		fmt.Println("错误:请提供-s或-c选项")
		printUsage()
	}
}
func Server(HpR1 string,HpUp string)(){
	ms:=&Server_t{}
	var err error
	ms.LnR1,err = net.Listen("tcp",HpR1)
	if err!=nil{
		fmt.Println("远程服务R1 No=",err)
	}
	fmt.Println("远程服务R1...")

	ms.LnUp,err = net.Listen("tcp",HpUp)
	if err!=nil{
		fmt.Println("远程服务Up No=",err)
	}
	fmt.Println("远程服务Up...")

	go ms.handleServerR1()
	ms.handleServerUp()


}
func (this * Server_t)handleServerR1()(){
	var err error
	for{

		this.ConnR1,err  = this.LnR1.Accept()
		if err != nil {
			fmt.Println("Accept error:", err)
			continue // 继续等待下一个连接请求
		}
		fmt.Println(this.ConnR1.RemoteAddr().String()," Connect ",this.ConnR1.LocalAddr().String())
		for{
			tf := &utils.Transfer{Conn: this.ConnR1,}
			data,err,offset,data2:=tf.ReadPkgNo()

			if err!=nil{
				fmt.Println("R R1 No=",err)
				if err == io.EOF { // 远程主机已经关闭连接
					fmt.Println("Connection closed by remote host")

				}
				break
			}

			switch data.Type{
			case utils.BackType:
				// fmt.Println(data)
				for {
					if this.ConnectedUp{
						this.ConnUp.Write(data.Data)
						if offset!=0{
							this.ConnUp.Write(data2.Data)
						}
						break
					}else{
						fmt.Println("未检测到Up连接 等待5秒")
						time.Sleep(5*time.Second)
					}
				}

			case utils.GoType:
				fmt.Println(data)
			default:
				fmt.Println(data)
			}
	
		}
		this.ConnR1.Close() // 关闭连接
	}

	
}
func (this * Server_t)handleServerUp()(){
	var err error
	for {
		this.ConnUp, err = this.LnUp.Accept()
		if err != nil {
			fmt.Println("Accept error:", err)
			continue // 继续等待下一个连接请求
		}
		fmt.Println(this.ConnUp.RemoteAddr().String(), " Connect ", this.ConnUp.LocalAddr().String())
		this.ConnectedUp = true
		for {
			n, err := this.ConnUp.Read(this.BufR1[:])
			if err != nil {
				fmt.Println("R Up No", err)
				if err == io.EOF { // 远程主机已经关闭连接
					fmt.Println("Connection closed by remote host")
				}
				this.ConnectedUp = false
				break // 退出当前循环并关闭连接
			}
			tf:=&utils.Transfer{Conn: this.ConnR1,}
			//发送返回包
			var GoMsg utils.Message
			GoMsg.Type = utils.GoType
			GoMsg.Data = this.BufR1[:n]
			GoMsgM,err:=json.Marshal(GoMsg)
			if err!=nil{
				fmt.Println("No=",err)
			}
			
			err = tf.WritePkgNo(GoMsgM)
			if err!=nil{
				fmt.Println("WNo=",err)
			}
			for i := range this.BufR1 {
				this.BufR1[i] = 0
			}

		}
	
		this.ConnUp.Close() // 关闭连接
	}
	
}
func Client(HpR1 string,HpLocal string)(){
	mc:=&Client_t{}

	go mc.handleR1(HpR1)
	mc.handleLocal(HpLocal)


}
func (this * Client_t)handleLocal(hp string){
	//做最大次数尝试
	var err error
	for{
		this.ConnLocal,err = net.Dial("tcp",hp)
		if err!=nil{
			fmt.Println("本地服务连接No=",err)
		}
		fmt.Println("本地服务已连接")
		for{
			n,err := this.ConnLocal.Read(this.BufR1[:])
			if err!=nil{
				fmt.Println("R Local No=",err)
				if err == io.EOF { // 远程主机已经关闭连接
					fmt.Println("Connection closed by remote host")
				}
				break
			}
	
			if this.ConnectedR1{
				tf:=&utils.Transfer{Conn: this.ConnR1,}
				//发送返回包
				var BackMsg utils.Message
				BackMsg.Type = utils.BackType
				BackMsg.Data = this.BufR1[:n]
				BackMsgM,err:=json.Marshal(BackMsg)
				if err!=nil{
					fmt.Println("No=",err)
				}
				fmt.Println("读到",n,"发送",len(BackMsgM))
				err = tf.WritePkgNo(BackMsgM)
				if err!=nil{
					fmt.Println("WNo=",err)
				}
				for i := range this.BufR1 {
					this.BufR1[i] = 0
				}
			}else{
				fmt.Println("未检测到R1连接 等待5秒")
				time.Sleep(5*time.Second)
			}

			
		}
		this.ConnLocal.Close()
	}
	
}
func (this * Client_t)handleR1(hp string){
	// defer HR.Done()
	var err error
	for{
		this.ConnR1,err = net.Dial("tcp",hp)
		if err!=nil{
			fmt.Println("远程服务R1 等待5秒 No=",err)
			time.Sleep(5*time.Second)
			continue
		}
		fmt.Println("远程服务已连接")
		this.ConnectedR1 = true
		for{
			
			tf := &utils.Transfer{Conn: this.ConnR1,}
			data,err,offset,data2:=tf.ReadPkgNo()
			if err!=nil{
				fmt.Println("R R1 No=",err)
				if err == io.EOF { // 远程主机已经关闭连接
					fmt.Println("Connection closed by remote host")
				}
				this.ConnectedR1 = false
				break

			}
			switch data.Type{
			case utils.BackType:
				fmt.Println(data)
			case utils.GoType:
				// fmt.Println(data)
				this.ConnLocal.Write(data.Data)
				if offset!=0{
					this.ConnLocal.Write(data2.Data)
				}
			default:
				fmt.Println(data)
			}
			
		}
		this.ConnR1.Close()
	}
	
}


package utils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net"
)
var (
	GoType string ="Go"
	BackType string = "Back"
)

type Message struct{
	Type string `json:"type"`
	Data []byte	`json:"data"`
}
type Transfer struct{
	Conn net.Conn	//文件描述符
	Buf [4096]byte //缓冲
}
func (this *Transfer)WritePkgNo(data []byte) (err error) {
    pkgLen := uint32(len(data))
    binary.BigEndian.PutUint32(this.Buf[:4], pkgLen)
    n := copy(this.Buf[4:], data)
	if n!=int(pkgLen){
		fmt.Println("Copy No=",n,"!=",int(pkgLen))
		return
	}
    lastLen :=int(pkgLen)+4
    // fmt.Println("W=",this.Buf[:4],string(this.Buf[4:lastLen]),int(pkgLen),string(data))
    fmt.Println("W=",int(pkgLen))
    n,err=this.Conn.Write(this.Buf[:lastLen])
    if err!=nil || int(lastLen)!=n{
        fmt.Println(err,int(lastLen),"!=",n)
        return
    }
    return
}

func (this *Transfer)ReadPkgNo()(msg Message,err error,offset int,msg2 Message){
    n,err:=this.Conn.Read(this.Buf[:])
    if err!=nil{
        fmt.Println("R No=",err)
        return
    }
    var pkgLen uint32
	pkgLen = binary.BigEndian.Uint32(this.Buf[:4])
    lastLen:=int(pkgLen)+4
    // fmt.Println("R=",this.Buf[:4],string(this.Buf[4:lastLen]),int(pkgLen),string(this.Buf[4:n]))
    fmt.Println("W=",int(pkgLen))
    if n!=lastLen{
        fmt.Println("[1]",int(lastLen),"!=",n)
        offset = lastLen
        var pkgLen2 uint32
	    pkgLen2 = binary.BigEndian.Uint32(this.Buf[offset:offset+4])
        if n!= offset+4+int(pkgLen2){
            fmt.Println("[2]",int(offset+4+int(pkgLen2)),"!=",n)
        }
        err = json.Unmarshal(this.Buf[offset+4:offset+4+int(pkgLen2)],&msg2)
        if err!=nil{
            fmt.Println("Unmarshal No=",err)
            return
        }
        fmt.Println("第二个包",msg2)
    }else{
        offset = 0
    }

    err = json.Unmarshal(this.Buf[4:lastLen],&msg)
    if err!=nil{
        fmt.Println("Unmarshal No=",err)
        return
    }
	return 
}

func (this *Transfer)WritePkg(data []byte) (err error) {
	//加密流程,长度+原始内容 -》 替换过的密钥(32)+长度(4)+加密内容
	pkgLen := uint32(len(data))
    fmt.Println("write n=",int(pkgLen))
	var BufTemp [4096]byte
	binary.BigEndian.PutUint32(BufTemp[:4], pkgLen)
	n := copy(BufTemp[4:], data)
	if n!=int(pkgLen){
		fmt.Println("WNo=",n,"!=",int(pkgLen))
		return
	}
	lastLenTemp := 4+n
	// fmt.Println(string(BufTemp[:]))
	//加密程序
	oldKey,newKey:= OutKey()
	// fmt.Println(string(oldKey),len(oldKey),string(newKey),len(newKey))
	ciphertext, err := EncryptAES(BufTemp[:lastLenTemp],[]byte(oldKey))
    if err != nil {
        fmt.Println("EncodeNo=",err)
		return
    }
	EnpkgLen := uint32(len(ciphertext))
	binary.BigEndian.PutUint32(this.Buf[32:36], EnpkgLen)
	copy(this.Buf[:32], newKey)
	n =copy(this.Buf[36:],ciphertext)
	lastLen := 36+n
	// fmt.Println(string(this.Buf[:32]),this.Buf[32:36],this.Buf[36:lastLen])
	//发包
	n, err = this.Conn.Write(this.Buf[:lastLen])
	if err != nil || n!=int(lastLen) {
		fmt.Println("WNo=", err,n,"!=",int(lastLen))
		return
	}

	//调试
	// fmt.Println("W=",this.Buf[:4],string(this.Buf[4:lastLen]),"n=",n)
	return
}
func (this *Transfer)ReadPkg()(msg Message,err error){
	//解密流程,替换过的密钥(32)+长度(4)+加密内容 -》  长度+原始内容 
	//fmt.Println("Reading client send message...")
	n,err:=this.Conn.Read(this.Buf[:])
	if err!=nil{
		return
	}
	if n<=32{
		err  = fmt.Errorf("The Readpkg is Null %v %v %v %v",string(this.Buf[:n]),this.Buf[:n],this.Conn.RemoteAddr().String(),this.Conn.LocalAddr().String())
		return
	}
	var EnpkgLen uint32
	EnpkgLen = binary.BigEndian.Uint32(this.Buf[32:36])
	newKey := string(this.Buf[:32])
	oldKey := KeyOut(newKey)
	// fmt.Println(newKey,oldKey,EnpkgLen)
	EncodeData := this.Buf[36:36+EnpkgLen]
	// fmt.Println(EncodeData)
	// decrypt ciphertext with AES-256-CBC
	decryptedPlaintext, err := DecryptAES(EncodeData, []byte(oldKey))
	if err != nil {
		fmt.Println("DecodeNo=",err)
		return
	}
	n = copy(this.Buf[:],decryptedPlaintext)
	// fmt.Println(string(decryptedPlaintext),n)
	//调试
	var pkgLen uint32
	pkgLen = binary.BigEndian.Uint32(this.Buf[:4])
	//调试
    fmt.Println("read n=",int(pkgLen))
	// fmt.Println("R=",this.Buf[:4],string(this.Buf[4:n]),"n=",n)
	if err!=nil||(n-4)!=int(pkgLen){
		fmt.Println("RNo=",err,(n-4),"!=",int(pkgLen))
		return
	}
	//调试
    err = json.Unmarshal(this.Buf[4:n],&msg)
    if err!=nil{
        fmt.Println("No=",err)
    }
	return 
}


func OutKey() (oldKey string, newKey string) {
    // 生成32字节的随机密钥
    key := make([]byte, 16)
    if _, err := rand.Read(key); err != nil {
        panic(err)
    }
    oldKey = hex.EncodeToString(key)

    // 转换为16进制字符串并替换字符
    replacementMap := map[rune]rune{
        '0': 'f',
        '1': 'e',
        '2': 'd',
        '3': 'c',
        '4': 'b',
        '5': 'a',
        '6': '9',
        '7': '8',
        '8': '7',
        '9': '6',
        'a': '5',
        'b': '4',
        'c': '3',
        'd': '2',
        'e': '1',
        'f': '0',
    }
    var buffer bytes.Buffer
    for _, r := range oldKey {
        if original, ok := replacementMap[r]; ok {
            buffer.WriteRune(original)
        } else {
            buffer.WriteRune(r)
        }
    }
    newKey = buffer.String()


    return
}

func KeyOut(hexKey string) string {
    // 构造字符替换表
    replacementMap := map[rune]rune{
        'f': '0',
        'e': '1',
        'd': '2',
        'c': '3',
        'b': '4',
        'a': '5',
        '9': '6',
        '8': '7',
        '7': '8',
        '6': '9',
        '5': 'a',
        '4': 'b',
        '3': 'c',
        '2': 'd',
        '1': 'e',
        '0': 'f',
    }

    // 转换为字节数组并替换字符
    keyBytes, err := hex.DecodeString(hexKey)
    if err != nil {
        panic(err)
    }
    var buffer bytes.Buffer
    for _, b := range keyBytes {
        hexStr := hex.EncodeToString([]byte{b})
        rune1 := rune(hexStr[0])
        rune2 := rune(hexStr[1])
        if original1, ok := replacementMap[rune1]; ok {
            buffer.WriteRune(original1)
        } else {
            buffer.WriteRune(rune1)
        }
        if original2, ok := replacementMap[rune2]; ok {
            buffer.WriteRune(original2)
        } else {
            buffer.WriteRune(rune2)
        }
    }
    replacedHexKey := buffer.String()

    return replacedHexKey
}
// EncryptAES encrypts plaintext using AES-256-CBC with the given key
func EncryptAES(plaintext []byte, key []byte) ([]byte, error) {
    // create AES cipher block
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    // pad plaintext with PKCS#7 padding
    paddedPlaintext := pkcs7Pad(plaintext, aes.BlockSize)

    // generate random IV
    iv := make([]byte, aes.BlockSize)
    if _, err := rand.Read(iv); err != nil {
        return nil, err
    }

    // create CBC mode encrypter
    cbc := cipher.NewCBCEncrypter(block, iv)

    // encrypt plaintext
    ciphertext := make([]byte, len(paddedPlaintext))
    cbc.CryptBlocks(ciphertext, paddedPlaintext)

    // append IV to the beginning of the ciphertext
    ciphertext = append(iv, ciphertext...)

    return ciphertext, nil
}

// DecryptAES decrypts ciphertext using AES-256-CBC with the given key
func DecryptAES(ciphertext []byte, key []byte) ([]byte, error) {
    if len(ciphertext) < aes.BlockSize*2 {
        return nil, errors.New("invalid ciphertext")
    }

    // extract IV from the beginning of the ciphertext
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    // create AES cipher block
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    // create CBC mode decrypter
    cbc := cipher.NewCBCDecrypter(block, iv)

    // decrypt ciphertext
    decryptedPlaintext := make([]byte, len(ciphertext))
    cbc.CryptBlocks(decryptedPlaintext, ciphertext)

    // unpad plaintext by removing PKCS#7 padding
    plaintext, err := pkcs7Unpad(decryptedPlaintext, aes.BlockSize)
    if err != nil {
        return nil, err
    }

    return plaintext, nil
}

func pkcs7Pad(data []byte, blockSize int) []byte {
    padding := blockSize - len(data)%blockSize
    padText := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(data, padText...)
}

func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
    length := len(data)
    if length == 0 {
        return nil, errors.New("empty data")
    }
    if length%blockSize != 0 {
        return nil, errors.New("invalid data length")
    }
    padding := int(data[length-1])
    if padding > blockSize || padding == 0 {
        return nil, errors.New("invalid padding")
    }
    for i := 1; i <= padding; i++ {
        if data[length-i] != byte(padding) {
            return nil, errors.New("invalid padding")
        }
    }
    return data[:length-padding], nil
}


代码还会出现一些问题,粘包问题,不能多个连接等问题

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

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

相关文章

什么是DevOps? 什么是DORA?

1. 前言 对于搞云原生应用的同学&#xff0c;对于DevOps和DORA应该都不陌生。但对于传统应用程序开发的同学&#xff0c;经常被DevOps, Microservice, CICD, DORA这些新颖的名词搞得晕头转向。那么到底什么是DevOps? 什么是DORA呢&#xff1f; 2. 解析 2.1 DevOps DevOps并…

群晖NAS搭建WebDV服务手机ES文件浏览器远程访问

文章目录 1. 安装启用WebDAV2. 安装cpolar3. 配置公网访问地址4. 公网测试连接5. 固定连接公网地址 转载自cpolar极点云文章&#xff1a;群晖NAS搭建WebDAV服务手机ES文件浏览器远程访问 有时候我们想通过移动设备访问群晖NAS 中的文件,以满足特殊需求,我们在群辉中开启WebDav服…

支持刷机(OpenWrt)的路由器大全

2023年上半年最热门的刷机路由器当然是360T7、小米WR30U这两款&#xff0c;主要是性价比高&#xff0c;闲鱼100多搞定&#xff0c;支持刷OpenWrt、支持WiFi6&#xff0c;采用MTK798X系列处理器&#xff0c;性能强&#xff0c;轻松跑满千兆&#xff0c;如果你想追新&#xff0c;…

SpringMVC基础知识

一、SpringMVC 1. Spring与Web环境集成 1.1 ApplicationContext应用上下文获取方式 应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的&#xff0c;但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件…

云捷|打破应用孤岛加速企业数字化转型

CBG云服务BU X 神州数码云基地 一、引言 从流程信息化到整体的数字化转型&#xff0c;对企业而言是一场深刻的升级再造。企业在决心开启数字化转型之路后&#xff0c;有近5成民营企业数字化转型采用标准化工具&#xff1b;关于“贵公司未来将在工作中增加哪些数字应用的使用…

强化学习从基础到进阶-案例与实践[4.2]:深度Q网络DQN-Cart pole游戏展示

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…

基于PaddleDetection fairmot目标跟踪 C++ 部署

1 源码下载 PaddleDetection 2 工程编译 参考&#xff1a;paddle 目标检测C部署流程 3 导出模型 python tools/export_model.py -c configs/mot/fairmot/fairmot_dla34_30e_576x320.yml --output_dir ./inference -o weightshttps://paddledet.bj.bcebos.com/models/mot/…

AutoSAR系列讲解(入门篇)4.7-BSW的Diagnostics功能

一、架构与术语解释 首先简单介绍以下诊断&#xff08;Diagnostics&#xff09;&#xff0c;由于百度百科中就有很好的解释&#xff0c;这里直接引用一下&#xff1a; 汽车诊断技术是凭借仪器设备对汽车进行性能测试和故障检查的方法和手段&#xff0c;它能够测试出汽车各项工作…

CMU 15-445 -- 存储篇 - 02

CMU 15-445 -- 存储篇 - 02 引言Database StorageDisk Manager 简介计算机存储体系为什么不使用 OS 自带的磁盘管理模块磁盘管理模块的核心问题在文件中表示数据库File StorageDatabase PagesHeap File OrganizationPage LayoutData LayoutTuple LayoutTuple Storage Data Repr…

Windows Update当前无法检查更新怎么办?

当进行Windows更新或升级时&#xff0c;可能会提示“Windows Update当前无法检查更新&#xff0c;因为未运行服务。您可能需要重新启动计算机”。而当重启也无法解决问题时&#xff0c;我们该怎么办呢&#xff1f;下面我们就来了解一下。 1、删除Software Distribution文件夹中…

Hyperledger Fabric交易流程分析

1、交易流程 客户端利用受支持的SDK(Golang、Java、Node、Python)提供的API构建交易提案请求&#xff0c;将交易事务提案打包成为一个正确的格式。交易提案包含如下要素&#xff1a; ①channelID:通道信息。 ②chaincodelD:要调用的链码信息。 ③timestamp:时间戳。 ④sign:客户…

百度智能车竞赛丝绸之路2——手柄控制

百度智能车竞赛丝绸之路1——智能车设计与编程实现控制 百度智能车竞赛丝绸之路2——手柄控制 一、机器人设计 二、实现原理 本教程使用Python的Serial库和Struct二进制数据解析库去实现Xbox手柄百度大脑学习开发板&#xff08;上位机&#xff09;和机器人控制器&#xff08;…

9小时通关 黑马新教程发布,含重磅项目~

随着测试行业的蓬勃发展&#xff0c;对从业者的要求越来越高&#xff0c;自动化测试已经成为软件测试中一个重要组成部分&#xff0c;广泛应用于各行各业。甚至&#xff0c;在圈子中还流传着这样一句话&#xff1a;学好测试自动化&#xff0c;年薪30万不在话下&#xff01; 今…

Qt读写文件

一、界面 项目文件结构 样例文件 中芯国际近期做出了两个重要改变&#xff1a;第一个是调整财报披露方式&#xff0c;不再公布芯片制程的营收占比&#xff0c;而只公布晶圆尺寸的营收占比&#xff1b;第二个是撤消14nm工艺的官方展示&#xff0c;只有28nm、40nm及以上的芯片工…

LeNet基础

目录 1.LeNet简介 1.1基本介绍 1.2网络结构 2.LetNet在pytorch中的使用 2.1首先定义模型 2.2初始化数据集&#xff0c;初始化模型&#xff0c;同时训练数据。 2.3 训练结果​编辑 2.4绘制曲线 1.LeNet简介 1.1基本介绍 LeNet&#xff08;LeNet-5&#xff09;是历史上第…

磁盘阵列(RAID)

什么是磁盘阵列 磁盘阵列&#xff08;RAID&#xff09;是一种将多个物理硬盘组合成一个逻辑存储单元的技术。这种技术可以提高数据存储的可靠性、性能或容量&#xff0c;并且可以在某些情况下提供备份和灾难恢复功能。 RAID技术可以通过在多个硬盘之间分配数据来提高性能。例…

事务处理相关

目录 步骤1.创建一个数据表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 开启事务 1…

电磁阀原理精髓

一、引用 电磁阀在液/气路系统中&#xff0c;用来实现液路的通断或液流方向的改变&#xff0c;它一般具有一个可以在线圈电磁力驱动下滑动的阀芯&#xff0c;阀芯在不同的位置时&#xff0c;电磁阀的通路也就不同。 阀芯在线圈不通电时处在甲位置&#xff0c;在线圈通电时处在…

算法与数据结构-链表

文章目录 链表和数组的区别常见的链表类型单链表循环链表双向链表 总结 链表和数组的区别 相比数组&#xff0c;链表是一种稍微复杂一点的数据结构。对于初学者来说&#xff0c;掌握起来也要比数组稍难一些。这两个非常基础、非常常用的数据结构&#xff0c;我们常常会放到一块…

Python基础 - global nonlocal

global global作为全局变量的标识符&#xff0c;修饰变量后标识该变量是全局变量 global关键字可以用在任何地方&#xff0c;包括最上层函数中和嵌套函数中 实例1&#xff1a;如下代码&#xff0c;定义了两个x&#xff0c;并且赋值不同 直接调用print(x) 打印的是全局变量x的…