package client import ( "bufio" "fmt" "io" "log/slog" "net" "os" "proxy-server/client/core" "proxy-server/client/geo" "proxy-server/client/report" "proxy-server/pkg/utils" "time" "errors" "github.com/joho/godotenv" _ "net/http/pprof" ) var Geo geo.Func = geo.Ipapi func Start() error { // 初始化环境变量 slog.SetLogLoggerLevel(slog.LevelDebug) err := godotenv.Load() if err != nil { slog.Debug("没有本地环境变量文件") } else { online := os.Getenv("ENDPOINT_ONLINE") if online != "" { core.EndpointOnline = online } offline := os.Getenv("ENDPOINT_OFFLINE") if offline != "" { core.EndpointOffline = offline } } // 性能监控 // go func() { // runtime.SetBlockProfileRate(1) // err := http.ListenAndServe(":7070", nil) // if err != nil { // slog.Error("性能监控服务启动失败", "err", err) // } // }() // 获取归属地 slog.Debug("获取节点归属地...") prov, city, isp, err := Geo() if err != nil { slog.Error("获取归属地失败", "err", err) } // 注册节点 slog.Debug("注册节点...") host, err := report.Online(prov, city, isp) if err != nil { slog.Error("节点注册失败", "err", err) return err } // 建立控制通道 for { err := ctrl(host) if err != nil { slog.Error("建立控制通道失败", "err", err) slog.Info(fmt.Sprintf("%d 秒后重试", core.RetryInterval)) time.Sleep(time.Duration(core.RetryInterval) * time.Second) } } } func ctrl(host string) error { ctrlAddr := net.JoinHostPort(host, fmt.Sprintf("%d", core.FwdCtrlPort)) dataAddr := net.JoinHostPort(host, fmt.Sprintf("%d", core.FwdDataPort)) slog.Info("建立控制通道", "addr", ctrlAddr) conn, err := net.Dial("tcp", ctrlAddr) if err != nil { return errors.New("连接失败") } defer utils.Close(conn) reader := bufio.NewReader(conn) // 请求转发端口 _, err = conn.Write([]byte{core.Version}) if err != nil { return errors.New("发送版本号失败") } // 发送客户端名称 nameLen := byte(len(core.Name)) nameBuf := make([]byte, 1+nameLen) nameBuf[0] = nameLen copy(nameBuf[1:], core.Name) _, err = conn.Write(nameBuf) if err != nil { return errors.New("发送 name 失败") } // 等待服务端响应 respBuf, err := reader.ReadByte() if err != nil { return errors.New("接收响应失败") } if respBuf != 1 { return errors.New("服务端响应失败") } else { slog.Info("成功建立连接") } // 等待用户连接 // 读写失败后退出重连,防止后续数据读写顺序错位导致卡死控制通道 slog.Info("等待用户连接") for { // 接收 dst dstLen, err := reader.ReadByte() if err != nil { return errors.New("接收 dstLen 失败") } dstBuf, err := utils.ReadBuffer(reader, int(dstLen)) if err != nil { return errors.New("接收 dstBuf 失败") } addr := string(dstBuf) // 接收 tag tagLen, err := reader.ReadByte() if err != nil { return errors.New("接收 tagLen 失败") } tagBuf, err := utils.ReadBuffer(reader, int(tagLen)) if err != nil { return errors.New("接收 tagBuf 失败") } // 建立数据通道 go func() { err := data(dataAddr, addr, tagBuf) if err != nil { slog.Error("建立数据通道失败", "err", err) } }() } } func data(dataAddr string, dest string, tag []byte) error { // 向服务端建立连接 src, err := net.Dial("tcp", dataAddr) if err != nil { return errors.New("连接服务端失败") } tagLen := byte(len(tag)) tagBuf := make([]byte, 2+tagLen) tagBuf[1] = tagLen copy(tagBuf[2:], tag) // 向目标地址建立连接 dst, dstErr := net.Dial("tcp", dest) if dstErr != nil { tagBuf[0] = 0 } else { tagBuf[0] = 1 } // 发送连接状态 _, err = src.Write(tagBuf) if err != nil { utils.Close(src) if dst != nil { utils.Close(dst) } return errors.New("发送连接状态失败") } if tagBuf[0] == 0 { utils.Close(src) if dst != nil { utils.Close(dst) } return errors.New("连接目标地址失败") } go func() { defer utils.Close(dst) _, err := io.Copy(dst, src) if err != nil && !errors.Is(err, net.ErrClosed) { slog.Error("上行流量代理失败", "err", err) } }() go func() { defer utils.Close(src) _, err := io.Copy(src, dst) if err != nil && !errors.Is(err, net.ErrClosed) { slog.Error("下行流量代理失败", "err", err) } }() return nil }