Go编写流量代理工具

这篇具有很好参考价值的文章主要介绍了Go编写流量代理工具。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这是一个演示

代理本地HTTP服务
Go编写流量代理工具
代理局域网SSH服务
Go编写流量代理工具
Go编写流量代理工具
其他的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
}


代码还会出现一些问题,粘包问题,不能多个连接等问题文章来源地址https://www.toymoban.com/news/detail-509229.html

到了这里,关于Go编写流量代理工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于Go编写一个可视化Navicat本地密码解析器

    开发小组在测试环境基于 docker 构建和迁移一个 MySQL8.x 实例,过程中大意没有记录对应的用户密码,然后发现某开发同事本地 Navicat 记录了根用户,于是搜索是否能够反解析 Navicat 中的密码掩码(这里可以基本断定 Navicat 对密码是采用了对称加密算法),于是发现了这个仓库

    2024年02月14日
    浏览(31)
  • Woodpecker CI 设计分析|一个 Go 编写的开源持续集成引擎

    大家好,这里是白泽。随着 Go 语言在云原生领域大放异彩,开发者逐渐将目光转移到了这门语言上,而容器则是云原生时代最核心的载体。 《Woodpecker CI 设计分析》系列文章将分析开源 CI 引擎 Woodpecker 的架构设计,探究 Go 协程是如何支持由 Workflow 定义的大量 Task 的频繁创建

    2024年01月22日
    浏览(24)
  • go入门实践四-go实现一个简单的tcp-socks5代理服务

    SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。协议在应用层和传输层之间。 本文使用先了解 socks 协议。然后实现一个socks5的tcp代理服务端。最后,进行抓包验证。 本文完整代码见仓库:laboratory/16-go-socks5 socks 协议相对http和tcp协议,还是比较

    2024年02月04日
    浏览(31)
  • 使用Python编写一个渗透测试探测工具

    本篇将会涉及: 资源探测 一个有用的字典资源 第一个暴力探测器 资源探测 资源探测在渗透测试中还是属于资源的映射和信息的收集阶段。 主要有以下三个类型: 字典攻击 暴力破解 模糊测试 字典攻击,在破解密码或密钥的时候,通过自定义的字典文件,有针对性地尝试字

    2024年01月16日
    浏览(35)
  • 又一个基于 GPT-4 编写、解释代码的工具,诞生了!

    作者:JackTian 来源:公众号「杰哥的IT之旅」 ID:Jake_Internet 链接:又一个基于 GPT-4 编写、解释代码的工具,诞生了! 上周,Open AI 团队正式宣布:GPT-4 来了! GPT-4 的出现,随后 Microsoft 的多个产品就集成了 GPT-4。 紧接着基于 Open AI 公司发布的 GPT-4 编写、编辑和讨论代码新一

    2023年04月18日
    浏览(26)
  • 用Go写一个缓存工具

    在项目发开过程中,一直有用到本地缓存和分布式本地缓存,最近从Java转到Go,也需要在Go里面重新实现下这个缓存工具。 本地缓存: 堆内缓存,访问速度快,系统重启后缓存清除。 分布式本地缓存 :分布式本地缓存本质上还是本地缓存,只有在分布式环境下,本地缓存数

    2024年02月04日
    浏览(23)
  • Istio是一个开源的基于 envoy proxy 的服务网格工具,它通过提供应用层面的流量管理和安全保障能力,帮助企业构建一个完整的服务网络体系

    作者:禅与计算机程序设计艺术 容器编排工具通常都提供微服务架构,其中包括服务注册与发现、负载均衡、流量控制和熔断等功能。随着云计算的普及,越来越多的人开始使用这些容器编排工具,包括Docker Swarm、Kubernetes、Mesos等。除了提供容器集群管理之外,许多容器编排

    2024年02月07日
    浏览(38)
  • 【单元测试】测还是不测,这是一个问题

    这篇文章也可以在我的博客中查看 相信大家从小就被千叮万嘱要做单元测试。然后秉承这一信念,成为了一个测试狂魔。凡有代码,测!覆盖!最终,一波操作猛如虎:467测试,0错误, 0自信 。 第二天。 你为了优化,颤抖着手更改了一行代码。果不其然发现牵连了 1e9 个测

    2024年02月03日
    浏览(36)
  • Qt编写的小软件:一个模拟按键按下和鼠标(左键)按下的小工具

    最近玩SLG游戏的时候有大量对剧情推进无意义的对话想要跳过的时候只能狂按空格键或者狂点鼠标,还好本人好歹是程序员,于是写了个小工具来处理。 下载地址:Qt编写的模拟鼠标按下和按键按下的小工具-C++文档类资源-CSDN下载 上面的资源包含打包好的程序和代码。 界面

    2024年02月11日
    浏览(35)
  • 传道授业20年,这是一个老网工的初心

    下午好,我是老杨。 昨天又是教师节 ,虽然是网工,但因为经常写写文章、聊聊技术,很多小友都会很客气地称我一声杨老师。 老杨收到了很多“教师节快乐”的私信,在此多谢各位小友的祝福 如果说当网工,是我蓄谋已久为之奋斗的事业,那输出技术干货、行业解读、和

    2024年02月09日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包