init
This commit is contained in:
commit
874d625f2d
BIN
4to6-linux-amd64
Normal file
BIN
4to6-linux-amd64
Normal file
Binary file not shown.
BIN
4to6-linux-arm64
Normal file
BIN
4to6-linux-arm64
Normal file
Binary file not shown.
9
4to6.conf
Normal file
9
4to6.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
global {
|
||||||
|
// 配置多个端口转发规则, 键是监听地址(IPv4), 值为目标地址(可以是IPv6).
|
||||||
|
|
||||||
|
// TCP 转发规则
|
||||||
|
LT = 0.0.0.0:2222 -> [2408:8221:9901:8895:4c32:7173:b476:b65c]:22
|
||||||
|
|
||||||
|
// UDP 转发规则
|
||||||
|
LU = 0.0.0.0:53 -> v6.aixiao.me:53
|
||||||
|
}
|
8
build.sh
Normal file
8
build.sh
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -x
|
||||||
|
|
||||||
|
BIN=4to6
|
||||||
|
|
||||||
|
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BIN}-linux-amd64 -a -ldflags '-extldflags "-static"'
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ${BIN}-linux-arm64 -a -ldflags '-extldflags "-static"'
|
139
config.go
Normal file
139
config.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config 结构体存储配置信息
|
||||||
|
type Config struct {
|
||||||
|
Global struct {
|
||||||
|
LT map[string]string // 存储本地地址到目标地址的映射
|
||||||
|
LU map[string]string // 存储本地地址到目标地址的映射
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidHostname 验证主机名格式
|
||||||
|
func isValidHostname(hostname string) bool {
|
||||||
|
if len(hostname) > 255 || hostname == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, part := range strings.Split(hostname, ".") {
|
||||||
|
if len(part) == 0 || len(part) > 63 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range part {
|
||||||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseAddress 解析并验证地址字符串
|
||||||
|
func parseAddress(address string) (string, int, error) {
|
||||||
|
host, portStr, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, fmt.Errorf("地址格式错误: %s (格式应为 host:port)", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil || port < 1 || port > 65535 {
|
||||||
|
return "", 0, fmt.Errorf("无效的端口号: %s", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
host = strings.Trim(host, "[]") // 去除 IPv6 方括号
|
||||||
|
if net.ParseIP(host) == nil && !isValidHostname(host) {
|
||||||
|
return "", 0, fmt.Errorf("无效的主机名或 IP: %s", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
return host, port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseConfig 解析配置文件
|
||||||
|
func parseConfig(filename string) (Config, error) {
|
||||||
|
var config Config
|
||||||
|
config.Global.LT = make(map[string]string)
|
||||||
|
config.Global.LU = make(map[string]string)
|
||||||
|
var currentSection string
|
||||||
|
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == "" || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(line, "{") {
|
||||||
|
currentSection = strings.Fields(line)[0]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if line == "}" {
|
||||||
|
currentSection = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.TrimSpace(parts[0])
|
||||||
|
value := strings.TrimSpace(strings.TrimSuffix(parts[1], ";"))
|
||||||
|
|
||||||
|
if currentSection == "global" && key == "LT" {
|
||||||
|
lsParts := strings.SplitN(value, "->", 2)
|
||||||
|
if len(lsParts) == 2 {
|
||||||
|
localAddr := strings.TrimSpace(lsParts[0])
|
||||||
|
targetAddr := strings.TrimSpace(lsParts[1])
|
||||||
|
|
||||||
|
if _, _, err := parseAddress(localAddr); err != nil {
|
||||||
|
return config, fmt.Errorf("本地地址格式错误: %s", localAddr)
|
||||||
|
}
|
||||||
|
if _, _, err := parseAddress(targetAddr); err != nil {
|
||||||
|
return config, fmt.Errorf("目标地址格式错误: %s", targetAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Global.LT[localAddr] = targetAddr
|
||||||
|
} else {
|
||||||
|
return config, fmt.Errorf("LT 配置格式错误: %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentSection == "global" && key == "LU" {
|
||||||
|
lsParts := strings.SplitN(value, "->", 2)
|
||||||
|
if len(lsParts) == 2 {
|
||||||
|
localAddr := strings.TrimSpace(lsParts[0])
|
||||||
|
targetAddr := strings.TrimSpace(lsParts[1])
|
||||||
|
|
||||||
|
if _, _, err := parseAddress(localAddr); err != nil {
|
||||||
|
return config, fmt.Errorf("本地地址格式错误: %s", localAddr)
|
||||||
|
}
|
||||||
|
if _, _, err := parseAddress(targetAddr); err != nil {
|
||||||
|
return config, fmt.Errorf("目标地址格式错误: %s", targetAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Global.LU[localAddr] = targetAddr
|
||||||
|
} else {
|
||||||
|
return config, fmt.Errorf("LU 配置格式错误: %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
51
daemon.go
Normal file
51
daemon.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Daemon 函数用于将程序转换为守护进程模式(简易实现)
|
||||||
|
// 注意:这不是 POSIX 标准的完整守护进程实现,缺少以下标准步骤:
|
||||||
|
// 1. 未创建新会话(setsid)
|
||||||
|
// 2. 未重置文件权限掩码
|
||||||
|
// 3. 未改变工作目录
|
||||||
|
// 4. 未关闭/重定向标准输入输出
|
||||||
|
// 适用于简单后台需求,生产环境建议使用专业库
|
||||||
|
func Daemon() {
|
||||||
|
// 构建新的命令行参数(过滤掉 -d 参数)
|
||||||
|
args := make([]string, 0)
|
||||||
|
for _, arg := range os.Args[1:] { // 遍历原始参数(跳过第一个程序路径参数)
|
||||||
|
if arg != "-d" { // 过滤掉用于触发守护进程的 -d 标志
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的进程命令
|
||||||
|
// os.Args[0] 是当前程序的执行路径
|
||||||
|
// args 是过滤后的新参数列表(不包含 -d)
|
||||||
|
cmd := exec.Command(os.Args[0], args...)
|
||||||
|
|
||||||
|
// 配置标准输入输出(注意:真正的守护进程通常需要重定向这些)
|
||||||
|
// 这里继承父进程的标准输入输出,实际使用时可能需要:
|
||||||
|
// 1. 重定向到文件
|
||||||
|
// 2. 指向 /dev/null
|
||||||
|
// 3. 使用日志系统
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// 异步启动新进程(Start 而非 Run)
|
||||||
|
// 注意:此处未设置新进程组或会话,可能导致子进程随终端关闭
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Fatalf("无法启动新实例: %v", err) // 启动失败时终止父进程
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出提示信息(调试用,正式版可删除)
|
||||||
|
fmt.Println("新实例已启动,当前进程将退出")
|
||||||
|
|
||||||
|
// 退出父进程,让子进程继续运行
|
||||||
|
// 注意:此时子进程的父进程变为 init 进程(PID 1)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
47
main.go
Normal file
47
main.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 主循环,为每个端口映射启动一个代理
|
||||||
|
func Loop(config Config) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// 启动 TCP 代理
|
||||||
|
for tcp_listenAddr, tcp_targetAddr := range config.Global.LT {
|
||||||
|
tcp_wg.Add(1)
|
||||||
|
go StartTcpProxy(tcp_listenAddr, tcp_targetAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 UDP 代理
|
||||||
|
for udp_listenAddr, udp_targetAddr := range config.Global.LU {
|
||||||
|
udp_wg.Add(1)
|
||||||
|
go StartUdpProxy(ctx, udp_listenAddr, udp_targetAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听退出信号,触发 `cancel()` 让所有 Goroutine 退出
|
||||||
|
WaitForExit(cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
daemon := flag.Bool("d", false, "守护进程模式") // 解析命令行参数,是否以守护进程模式运行
|
||||||
|
config := flag.String("c", "4to6.conf", "指定配置文件") // 解析命令行参数,是否以守护进程模式运行
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *daemon {
|
||||||
|
Daemon() // 如果设置了-d参数,则进入守护进程模式
|
||||||
|
}
|
||||||
|
|
||||||
|
LT, err := parseConfig(*config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "读取配置错误: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
Loop(LT) // 启动所有代理服务
|
||||||
|
|
||||||
|
}
|
137
tcp.go
Normal file
137
tcp.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tcp_wg sync.WaitGroup
|
||||||
|
tcp_listeners []net.Listener
|
||||||
|
tcp_listenMux sync.Mutex // 确保 tcp_listeners 操作是线程安全的
|
||||||
|
)
|
||||||
|
|
||||||
|
// 启用 TCP KeepAlive
|
||||||
|
func setKeepAlive(conn net.Conn) {
|
||||||
|
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||||
|
tcpConn.SetKeepAlive(true)
|
||||||
|
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭写方向
|
||||||
|
func closeWrite(conn net.Conn) {
|
||||||
|
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||||
|
tcpConn.CloseWrite()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理客户端连接,并将数据转发到目标服务器
|
||||||
|
func HandleTcpConnection(clientConn net.Conn, targetAddr string) {
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
// 连接目标服务器,支持 IPv6
|
||||||
|
serverConn, err := net.Dial("tcp", targetAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("无法连接到 %s: %v\n", targetAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer serverConn.Close()
|
||||||
|
|
||||||
|
// 开启 TCP KeepAlive,防止连接过早断开
|
||||||
|
setKeepAlive(clientConn)
|
||||||
|
setKeepAlive(serverConn)
|
||||||
|
|
||||||
|
clientAddr := clientConn.RemoteAddr().String()
|
||||||
|
fmt.Printf("连接 %s -> %s\n", clientAddr, targetAddr)
|
||||||
|
|
||||||
|
var cpWG sync.WaitGroup
|
||||||
|
cpWG.Add(2)
|
||||||
|
|
||||||
|
// 客户端 -> 目标服务器
|
||||||
|
go func() {
|
||||||
|
defer cpWG.Done()
|
||||||
|
_, err := io.Copy(serverConn, clientConn)
|
||||||
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
|
fmt.Printf("数据转发 %s -> %s 失败: %v\n", clientAddr, targetAddr, err)
|
||||||
|
}
|
||||||
|
closeWrite(serverConn)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 目标服务器 -> 客户端
|
||||||
|
go func() {
|
||||||
|
defer cpWG.Done()
|
||||||
|
_, err := io.Copy(clientConn, serverConn)
|
||||||
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
|
fmt.Printf("数据转发 %s <- %s 失败: %v\n", clientAddr, targetAddr, err)
|
||||||
|
}
|
||||||
|
closeWrite(clientConn)
|
||||||
|
}()
|
||||||
|
|
||||||
|
cpWG.Wait()
|
||||||
|
fmt.Printf("连接 %s 结束\n", clientAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动代理服务器
|
||||||
|
func StartTcpProxy(listenAddr, targetAddr string) {
|
||||||
|
defer tcp_wg.Done()
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("监听 %s 失败: %v\n", listenAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_listenMux.Lock()
|
||||||
|
tcp_listeners = append(tcp_listeners, listener)
|
||||||
|
tcp_listenMux.Unlock()
|
||||||
|
|
||||||
|
fmt.Printf("代理服务启动: %s -> %s\n", listenAddr, targetAddr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
clientConn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, net.ErrClosed) {
|
||||||
|
fmt.Printf("监听器 %s 已关闭\n", listenAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("接受连接失败: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go HandleTcpConnection(clientConn, targetAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听系统信号,以便优雅地关闭 TCP 和 UDP 代理服务
|
||||||
|
func WaitForExit(cancel context.CancelFunc) {
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
<-sigChan
|
||||||
|
fmt.Println("\n收到终止信号,准备关闭代理服务器...")
|
||||||
|
|
||||||
|
// 关闭所有 TCP 监听器
|
||||||
|
for _, listener := range tcp_listeners {
|
||||||
|
if err := listener.Close(); err != nil {
|
||||||
|
fmt.Printf("关闭 TCP 监听器 %s 失败: %v\n", listener.Addr(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发 UDP 代理退出
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
// 等待所有 TCP 和 UDP 代理退出
|
||||||
|
tcp_wg.Wait()
|
||||||
|
udp_wg.Wait()
|
||||||
|
|
||||||
|
fmt.Println("所有代理已安全退出")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
124
udp.go
Normal file
124
udp.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
udp_wg sync.WaitGroup
|
||||||
|
clientMap sync.Map // 代替 sync.Mutex 保护的 map[string]*net.UDPConn
|
||||||
|
)
|
||||||
|
|
||||||
|
// 处理 UDP 数据转发
|
||||||
|
func handleUdpTraffic(listener *net.UDPConn, targetConn *net.UDPConn, clientAddr *net.UDPAddr, data []byte) {
|
||||||
|
// 目标服务器超时设置,防止阻塞
|
||||||
|
targetConn.SetWriteDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err := targetConn.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("UDP 数据转发失败 %s -> %s: %v\n", clientAddr, targetConn.RemoteAddr(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取目标服务器响应
|
||||||
|
targetConn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
buffer := make([]byte, 4096)
|
||||||
|
n, _, err := targetConn.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("UDP 读取目标服务器响应失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送响应回客户端
|
||||||
|
listener.SetWriteDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err = listener.WriteToUDP(buffer[:n], clientAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("UDP 发送响应失败: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 UDP 代理
|
||||||
|
func StartUdpProxy(ctx context.Context, listenAddr, targetAddr string) {
|
||||||
|
defer udp_wg.Done()
|
||||||
|
|
||||||
|
localAddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("解析本地 UDP 地址 %s 失败: %v\n", listenAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.ListenUDP("udp", localAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("监听 UDP %s 失败: %v\n", listenAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
targetUDPAddr, err := net.ResolveUDPAddr("udp", targetAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("解析目标 UDP 地址 %s 失败: %v\n", targetAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("UDP 代理启动: %s -> %s\n", listenAddr, targetAddr)
|
||||||
|
|
||||||
|
// 设置多个 goroutine 处理 UDP 请求,提高吞吐量
|
||||||
|
numWorkers := 4 // 线程数,可根据 CPU 资源调整
|
||||||
|
for i := 0; i < numWorkers; i++ {
|
||||||
|
udp_wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer udp_wg.Done()
|
||||||
|
buffer := make([]byte, 4096)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
listener.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||||
|
n, clientAddr, err := listener.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("UDP 读取数据失败: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找或创建目标连接
|
||||||
|
clientKey := clientAddr.String()
|
||||||
|
targetConn, exists := clientMap.Load(clientKey)
|
||||||
|
if !exists {
|
||||||
|
targetConn, err = net.DialUDP("udp", nil, targetUDPAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("连接 UDP 目标 %s 失败: %v\n", targetAddr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clientMap.Store(clientKey, targetConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 UDP 数据
|
||||||
|
go handleUdpTraffic(listener, targetConn.(*net.UDPConn), clientAddr, buffer[:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待退出信号
|
||||||
|
<-ctx.Done()
|
||||||
|
fmt.Println("\n收到退出信号,正在关闭 UDP 代理...")
|
||||||
|
|
||||||
|
listener.Close()
|
||||||
|
clientMap.Range(func(key, value interface{}) bool {
|
||||||
|
value.(*net.UDPConn).Close()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("UDP 代理已关闭")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user