Go 语言的实战案例 SOCKS5 代理 | 青训营

news2024/11/18 11:43:48

Powered by:NEFU AB-IN

文章目录

  • Go 语言的实战案例 SOCKS5 代理 | 青训营
    • 引入
    • TCP echo server
    • auth 认证
    • 请求阶段
    • relay阶段

Go 语言的实战案例 SOCKS5 代理 | 青训营

  • GO语言工程实践课后作业:实现思路、代码以及路径记录

引入

代理是指在计算机网络中,代理服务器充当客户端和目标服务器之间的中介。它接收来自客户端的请求,然后将请求转发给目标服务器,再将目标服务器的响应返回给客户端。

用途

  • 匿名浏览:Socks5代理可以隐藏客户端的真实IP地址,使得客户端在访问目标服务器时更加匿名。可以代表客户端向其他服务器发送请求

    • 在网络爬虫中,使用代理服务器可以解决IP访问频率限制问题,因为它们可以隐藏真实的客户端IP地址并提供新的IP地址来进行请求,从而减轻对单个IP的访问频率限制。这样,爬虫就可以通过多个代理IP来轮流发送请求,避免被目标服务器封禁。
  • 数据加密:Socks5代理支持对数据流进行加密传输,提供更高的数据安全性。

  • 跨协议代理:Socks5代理不仅支持HTTP协议,还支持包括FTP、SMTP等在内的多种协议,实现了更广泛的应用场景。

  • 穿越防火墙:由于Socks5代理更通用,不受网络防火墙的限制,可以更容易地穿越网络防火墙,访问被封锁或限制的资源。

Socks5代理是一种代理协议,用于在网络上进行数据传输。Socks5代理服务器可以支持多种协议的代理,包括HTTP、FTP、SMTP等。与HTTP代理相比,Socks5代理更加灵活和通用。

主要区别如下:

  • HTTP代理:主要用于HTTP请求,支持浏览器的代理设置,透明代理
  • Socks5代理:支持HTTP以外的其他协议的代理,不支持浏览器的代理设置,需要在应用程序中设置代理,Socks5代理的主要特点是它可以在客户端和代理服务器之间建立一个 TCP 或 UDP 连接,并直接将数据转发给目标服务器,而不需要解析或修改数据包。这种特性使得SOCKS5代理适用于很多不同类型的网络应用,如 P2P 文件共享、实时视频传输等。

代理过程通常如下:

  1. 客户端向代理服务器发送连接请求,并指定目标服务器的地址。
  2. 代理服务器接收到请求后,解析目标服务器地址,并建立与目标服务器的连接。
  3. 代理服务器将客户端的请求转发给目标服务器。
  4. 目标服务器接收到请求后,处理请求并生成响应。
  5. 代理服务器将目标服务器的响应转发给客户端。
  6. 客户端接收到代理服务器返回的响应,并进行处理。

image.png
总的来说,代理服务器充当了客户端和目标服务器之间的中转站,使得客户端能间接与目标服务器进行通信,提供了更多的隐私保护和功能扩展。

TCP echo server

/*
 * @Author: NEFU AB-IN
 * @Date: 2023-08-23 22:36:26
 * @FilePath: \GoTest\6\6.go
 * @LastEditTime: 2023-08-23 22:43:57
 */
package main

import (
	"bufio"
	"log"
	"net"
)

// TCP echo server

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080") // 监听端口,返回一个server
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept() // 在死循环里,用这个方法接收一个请求
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client) // 处理这个链接 go关键字 启动子线程去处理
		// 如果接受连接时出现错误,服务器会记录错误信息,然后继续等待下一个客户端连接
		// 否则为每个客户端连接启动一个独立的process协程来处理客户端请求,这样可以并发处理多个客户端连接。
	}
}

func process(conn net.Conn) {
	defer conn.Close()              // 最后将连接关掉
	reader := bufio.NewReader(conn) // 基于这个链接,创造一个只读的带缓冲的流
	for {
		b, err := reader.ReadByte() // 每次读一个字节
		if err != nil {
			break
		}
		_, err = conn.Write([]byte{b}) // 将读取的字节回显给客户端,直接写回到连接中
		if err != nil {
			break
		}
	}
}

image.png

auth 认证

认证阶段:实现auth函数,更改process函数,里面用到auth函数

/*
 * @Author: NEFU AB-IN
 * @Date: 2023-08-23 22:50:05
 * @FilePath: \GoTest\7\7.go
 * @LastEditTime: 2023-08-23 22:50:11
 */
package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
)
// 一些常量定义,用于表示Socks5协议中的一些数值,例如协议版本、命令类型和地址类型等。
const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {

// 第一步:浏览器向代理服务器发送报文,三个字段
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。
  // RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD


// 前两个字段都是单字节,所以从流中单独读出
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize) // 创建缓冲区
	_, err = io.ReadFull(reader, method) // 它会从r中连续读取数据,直到buf被填满,或者遇到了一个错误。如果读取完整的buf之前发生错误,函数会返回读取的字节数n和错误信息err;如果成功读取了整个buf,函数会返回buf的长度和nil。
// 这个函数在需要确保读取完整数据包时很有用,比如读取固定长度的消息、解码二进制数据等。
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}
	log.Println("ver", ver, "method", method)
	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

请求阶段

请求阶段:实现connect函数,签名一致,在process中调用

package main

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}

	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
// 第二步:浏览器发送报文,包含六个字段
	// +----+-----+-------+------+----------+----------+
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节


// !!!! 可以看到前四个字段都是单字节,可以采用之前的方法一个个读出来,这里直接构造一个4字节的缓冲区,用readfull全读出来
	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("read header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}
	addr := ""
	switch atyp {
	case atypeIPV4: // 若是IPV4 读四个字节
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp failed:%w", err)
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
	case atypeHOST: // 若是域名,先读长度,构造缓冲区,最后转换为字符串
		hostSize, err := reader.ReadByte()
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize)
		_, err = io.ReadFull(reader, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
		}
		addr = string(host)
	case atypeIPV6: // 先不实现IPV6
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}
	_, err = io.ReadFull(reader, buf[:2]) // 这里复用之前的缓冲区,切片效果,只用前两位,填充满
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])  // 用于将 2 字节的大端序字节序列解码为一个无符号 16 位整数(uint16)。

	log.Println("dial", addr, port)

// 给予回报
	// +----+-----+-------+------+----------+----------+
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0}) // 拼成byte数组
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}
	return nil
}

relay阶段

relay阶段:最后一步,真正和服务器建立TCP连接,在 connect 函数中,增加功能

package main

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}
	for {
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		go process(client)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed:%w", err)
	}

	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	// +----+-----+-------+------+----------+----------+
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节

	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("read header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}
	addr := ""
	switch atyp {
	case atypeIPV4:
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp failed:%w", err)
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
	case atypeHOST:
		hostSize, err := reader.ReadByte()
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize)
		_, err = io.ReadFull(reader, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
		}
		addr = string(host)
	case atypeIPV6:
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}
	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	// 建立浏览器和服务器的双向数据转换,使用 `net.Dial` 函数连接到目标服务器 `dest`,即客户端所请求的远程服务器。这样就建立了代理服务器与目标服务器之间的连接。
	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
	if err != nil {
		return fmt.Errorf("dial dst failed:%w", err)
	}
	defer dest.Close()
	log.Println("dial", addr, port)

	// +----+-----+-------+------+----------+----------+
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT

 // 返回连接成功的报文
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}
        
        
// 这段代码的主要功能是实现两个goroutine并行执行io.Copy操作,并在其中一个goroutine完成后取消另一个goroutine的执行。
//- 第一行创建一个新的上下文对象ctx,并使用context.Background()作为根上下文对象。这个上下文对象用于在不同goroutine之间传递取消信号。
//- 第二行定义了一个cancel函数,用于取消上下文对象ctx。defer关键字用于延迟执行cancel函数,在函数返回时自动调用。
//- 第四行开启了一个goroutine,该goroutine负责将读取到的数据从reader复制到dest,并在复制完成后调用cancel函数取消上下文对象的执行。这样做是为了在复制完成后,能正确地通知另一个goroutine停止执行。
//- 第六行开启了第二个goroutine,该goroutine负责将dest的数据复制到conn,并在复制完成后也调用cancel函数取消上下文对象的执行。
//- 第九行使用<-ctx.Done()语法,表示等待上下文对象ctx被取消。一旦ctx被取消,即ctx.Done()返回,程序会继续往下执行。

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
 
	go func() {
		_, _ = io.Copy(dest, reader)
		cancel()
	}()
	go func() {
		_, _ = io.Copy(conn, dest)
		cancel()
	}()

	<-ctx.Done() // 阻塞等待,直到两个数据转发的Go协程都完成后,函数返回。执行时机,也就是cancel的时间。
	return nil
}

ctx, cancel := context.WithCancel(context.Background())解释

  • 上下文对象是 Go 语言中用于跨 goroutine 传递请求的值、截止时间以及取消信号的一种机制。它可以用来解决在并发环境下协调多个 goroutine 之间的操作和通信的问题。

  • 上下文对象通常用于以下几种情况:

    1. 传递请求的相关值:例如请求ID、用户信息等,在多个 goroutine 之间共享。
    2. 设置截止时间:对某些操作设置一个超时时间,用于控制操作的执行时间。
    3. 取消信号:当一个操作出现问题或者不再需要执行时,可以通过取消上下文对象来通知相关的 goroutine 停止执行。
  • context.WithCancel(context.Background()) 这句代码的意思是创建一个具有取消功能的上下文对象(WithCancel()函数接受一个 Context 并返回其子Context和取消函数cancel)。context.Background() 创建一个根上下文对象,而 context.WithCancel 则基于根上下文对象创建一个新的上下文对象,该上下文对象具有取消功能。返回的 ctx 和 cancel 是用于取消该上下文对象的函数。

  • 所以这句代码的作用就是创建一个新的上下文对象 ctx,并使用 context.WithCancel 函数将其绑定到一个取消函数 cancel 上。这样可以通过调用 cancel 函数来取消该上下文对象的执行。该上下文对象可以被传递给其他需要依赖上下文的 goroutine 来实现协作和取消的功能。

image.png

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

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

相关文章

硬件知识积累 LED的介绍与选型 (简单电路)

1. LED 的介绍 1.1 LED 是什么 LED :是一种能发光的半导体电子元件。发光二极管&#xff08;LED&#xff09;于20世纪60年代问世。在20世纪80年代之前&#xff0c;LED主要作为指示灯使用&#xff0c;从其光色来看&#xff0c;只有红光、橙光、黄光和绿光等几种。这一时期属于…

MyBatis动态语句且如何实现模糊查询及resultType与resultMap的区别---详细介绍

前言 前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用&#xff0c;因为通常查询条件是多样化的&#xff0c;需要根据实际情况来拼接SQL语句&#xff0c;那什么是MyBatis动态语句呢…

【Docker入门第一篇】

Docker简介 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使…

mysql数据备份批处理文件正式版已测试通过

这里写目录标题 1.数据库全表原始数据.ibd文件备份为.sql文件1.1理解date含义 2.备份原始表中部分表 --改进版2.1 backPartTable表内容2.2备份运行2.3实操代码 3.如何将备份的文件进行压缩处理&#xff1f;&#xff1f;执行压缩文件 1.数据库全表原始数据.ibd文件备份为.sql文件…

Android相机-HAL-Rockchip-hal3

引言&#xff1a; 对于Android相机的 HAL层而言对上实现一套Framework的API接口&#xff0c;对下通过V4L2框架实现与kernel的交互。不同的平台会有不同的实现方案。主要是对Android HAL3的接口的实现。看看rockchip是怎么支持hal3的&#xff1f; 代码目录&#xff1a; hardw…

浅析Linux追踪技术之kprobe:基于kprobes的Event Tracing

文章目录 概述内核选项配置ftrace配置接口kprobe事件配置 使用示例添加kprobes事件kprobes事件使能kprobes事件统计 参考链接 概述 常规的Event Tracing&#xff08;事件追踪&#xff09;是通过散落在Linux内核代码各处的Tracepoint来实现的&#xff0c;这些Tracepoint数量有限…

Docker部署MongoDB 5.0.5

1、查看目录 rootwielun:~# tree mongo mongo ├── conf │ └── mongod.conf ├── data ├── docker-compose.yml └── logrootwielun:~# cd mongo rootwielun:~/mongo# chmod 777 log2、配置docker-compose.yml rootwielun:~/mongo# cat docker-compose.yml ve…

RK3568 uart串口

一.简介 串口全称叫做串行接口&#xff0c;通常也叫做 COM 接口&#xff0c;串行接口指的是数据一个一个的顺序传 输&#xff0c;通信线路简单。使用两条线即可实现双向通信&#xff0c;一条用于发送&#xff0c;一条用于接收。串口通信 距离远&#xff0c;但是速度相对会低&a…

YOLOv5算法改进(3)— 添加CBAM注意力机制

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。注意力机制是近年来深度学习领域内的研究热点&#xff0c;可以帮助模型更好地关注重要的特征&#xff0c;从而提高模型的性能。CBAM&#xff08;Convolutional Block Attention Module&#xff09; 是一种用于前馈卷积神经…

Node基础--Node简介以及安装教程

1.Node简介 Node.js发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c;让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Pe…

使用Maven父工程构建spring boot子工程

1.父工程删除src目录&#xff0c;pom文件配置parent为spring-boot-starter-parent 2.创建子工程&#xff0c;子工程引入一个springboot相关依赖 注意&#xff1a;子工程引入springboot相关依赖之后子工程才能被解析为springboot模块

Android SDK 上手指南||第五章 用户界面设计

第五章 用户界面设计 在本篇教程中我们将为应用程序项目添加布局方案&#xff0c;在这方面XML与Eclipse ADT接口将成为工作中的得力助手——不过在后面两节中还会用到一部分Java开发知识。XML与Java在Android平台的开发工作当中可谓无处不在&#xff0c;如果大家对二者还缺乏基…

Windows运行Spark所需的Hadoop安装

解压文件 复制bin目录 找到winutils-master文件hadoop对应的bin目录版本 全部复制替换掉hadoop的bin目录文件 复制hadoop.dll文件 将bin目录下的hadoop.dll文件复制到System32目录下 配置环境变量 修改hadoop-env.cmd配置文件 注意jdk装在非C盘则完全没问题&#xff0c;如果装在…

绕过__wakeup() 反序列化 合集

变量引用 这个其实不是语言特性漏洞&#xff0c;而是代码逻辑漏洞。只有在特定代码情况下才会产生 KaTeX parse error: Expected EOF, got & at position 3: x&̲a使两个变量同时指向同一个内存地址 利用&#xff1a; KaTeX parse error: Expected EOF, got & a…

ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564)

安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564) 二、 CVE-2017-7564 一、ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564) Title 启用安全自托管侵入式调试接口&#xff0c;可允许非安全世界引发安全世界panic CV…

路径总和 III-前缀和dfs

给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到子节点&…

汽车电子笔记之:AUTOSAR方法论及基础概念

目录 1、AUTOSAR方法论 2、AUTOSAR的BSW 2.1、MCAL 2.2、ECU抽象层 2.3、服务层 2.4、复杂驱动 3、AUTOSAR的RTE 4、AUTOSAR的应用层 4.1、SWC 4.2、AUTOSAR的通信 4.3、AUTOSAR软件接口 1、AUTOSAR方法论 AUTOSAR为汽车电子软件系统开发过程定义了一套通用的技术方法…

Cyanine3 NHS ester生物分子的标记与共价结合1032678-38-8

​欢迎来到星戈瑞荧光stargraydye&#xff01;小编带您盘点&#xff1a; Cyanine3 NHS ester是一种荧光染料&#xff0c;可用于将含有游离氨基&#xff08;-NH2&#xff09;的生物分子如蛋白质、抗体、肽段、核酸等进行标记和共价结合。这个过程通常称为NHS酯化反应&#xff0c…

归并排序之从微观看递归

前言 这次&#xff0c;并不是具体讨论归并排序算法&#xff0c;而是利用归并排序算法&#xff0c;探讨一下递归。归并排序的特点在于连续使用了两次递归调用&#xff0c;这次我们将从微观上观察递归全过程&#xff0c;从本质上理解递归&#xff0c;如果能看完&#xff0c;你一…

LLMs领域适应的预训练Pre-training for domain adaptation

到目前为止&#xff0c;我强调了在开发应用程序时&#xff0c;您通常会使用现有的LLM。这为您节省了大量时间&#xff0c;并可以更快地得到一个工作原型。 但是&#xff0c;有一种情况下&#xff0c;您可能会发现有必要从头开始预训练自己的模型。如果您的目标领域使用的词汇和…