github.com/nxtrace/NTrace-core@v1.3.1-0.20240513132635-39169291e8c9/wshandle/client.go (about) 1 package wshandle 2 3 import ( 4 "crypto/tls" 5 "github.com/nxtrace/NTrace-core/pow" 6 "github.com/nxtrace/NTrace-core/util" 7 "log" 8 "net" 9 "net/http" 10 "net/url" 11 "os" 12 "os/signal" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/gorilla/websocket" 18 ) 19 20 type WsConn struct { 21 Connecting bool 22 Connected bool // 连接状态 23 MsgSendCh chan string // 消息发送通道 24 MsgReceiveCh chan string // 消息接收通道 25 Done chan struct{} // 发送结束通道 26 Exit chan bool // 程序退出信号 27 Interrupt chan os.Signal // 终端中止信号 28 Conn *websocket.Conn // 主连接 29 ConnMux sync.Mutex // 连接互斥锁 30 } 31 32 var wsconn *WsConn 33 var host, port, fastIp string 34 var envToken = util.EnvToken 35 var cacheToken string 36 var cacheTokenFailedTimes int 37 38 func (c *WsConn) keepAlive() { 39 go func() { 40 // 开启一个定时器 41 for { 42 <-time.After(time.Second * 54) 43 if c.Connected { 44 err := c.Conn.WriteMessage(websocket.TextMessage, []byte("ping")) 45 if err != nil { 46 log.Println(err) 47 c.Connected = false 48 return 49 } 50 } 51 } 52 }() 53 for { 54 if !c.Connected && !c.Connecting { 55 c.Connecting = true 56 c.recreateWsConn() 57 // log.Println("WebSocket 连接意外断开,正在尝试重连...") 58 // return 59 } 60 // 降低检测频率,优化 CPU 占用情况 61 <-time.After(200 * time.Millisecond) 62 } 63 } 64 65 func (c *WsConn) messageReceiveHandler() { 66 // defer close(c.Done) 67 for { 68 if c.Connected { 69 _, msg, err := c.Conn.ReadMessage() 70 if err != nil { 71 // 读取信息出错,连接已经意外断开 72 // log.Println(err) 73 c.Connected = false 74 return 75 } 76 if string(msg) != "pong" { 77 c.MsgReceiveCh <- string(msg) 78 } 79 } 80 } 81 } 82 83 func (c *WsConn) messageSendHandler() { 84 for { 85 // 循环监听发送 86 select { 87 case <-c.Done: 88 log.Println("go-routine has been returned") 89 return 90 case t := <-c.MsgSendCh: 91 // log.Println(t) 92 if !c.Connected { 93 c.MsgReceiveCh <- `{"ip":"` + t + `", "asnumber":"API Server Error"}` 94 } else { 95 err := c.Conn.WriteMessage(websocket.TextMessage, []byte(t)) 96 if err != nil { 97 log.Println("write:", err) 98 return 99 } 100 } 101 // 来自终端的中断运行请求 102 case <-c.Interrupt: 103 // 向 websocket 发起关闭连接任务 104 err := c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) 105 if err != nil { 106 // log.Println("write close:", err) 107 os.Exit(1) 108 } 109 select { 110 // 等到了结果,直接退出 111 case <-c.Done: 112 // 如果等待 1s 还是拿不到结果,不再等待,超时退出 113 case <-time.After(time.Second): 114 } 115 os.Exit(1) 116 // return 117 } 118 } 119 } 120 121 func (c *WsConn) recreateWsConn() { 122 // 尝试重新连线 123 u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"} 124 // log.Printf("connecting to %s", u.String()) 125 jwtToken, ua := envToken, []string{"Privileged Client"} 126 err := error(nil) 127 if envToken == "" { 128 // 无环境变量 token 129 if cacheToken == "" { 130 // 无cacheToken, 重新获取 token 131 if util.GetPowProvider() == "" { 132 jwtToken, err = pow.GetToken(fastIp, host, port) 133 } else { 134 jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port) 135 } 136 if err != nil { 137 log.Println(err) 138 os.Exit(1) 139 } 140 } else { 141 // 使用 cacheToken 142 jwtToken = cacheToken 143 } 144 ua = []string{util.UserAgent} 145 } 146 cacheToken = jwtToken 147 requestHeader := http.Header{ 148 "Host": []string{host}, 149 "User-Agent": ua, 150 "Authorization": []string{"Bearer " + jwtToken}, 151 } 152 dialer := websocket.DefaultDialer 153 dialer.TLSClientConfig = &tls.Config{ 154 ServerName: host, 155 } 156 proxyUrl := util.GetProxy() 157 if proxyUrl != nil { 158 dialer.Proxy = http.ProxyURL(proxyUrl) 159 } 160 ws, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader) 161 c.Conn = ws 162 if err != nil { 163 log.Println("dial:", err) 164 // <-time.After(time.Second * 1) 165 c.Connected = false 166 c.Connecting = false 167 if cacheTokenFailedTimes > 3 { 168 cacheToken = "" 169 } 170 cacheTokenFailedTimes += 1 171 //fmt.Println("重连失败", cacheTokenFailedTimes, "次") 172 return 173 } else { 174 c.Connected = true 175 } 176 c.Connecting = false 177 178 c.Done = make(chan struct{}) 179 go c.messageReceiveHandler() 180 } 181 182 func createWsConn() *WsConn { 183 proxyUrl := util.GetProxy() 184 //fmt.Println("正在连接 WS") 185 // 设置终端中断通道 186 interrupt := make(chan os.Signal, 1) 187 signal.Notify(interrupt, os.Interrupt) 188 host, port = util.GetHostAndPort() 189 // 如果 host 是一个 IP 使用默认域名 190 if valid := net.ParseIP(host); valid != nil { 191 fastIp = host 192 if len(strings.Split(fastIp, ":")) > 1 { 193 fastIp = "[" + fastIp + "]" 194 } 195 host = "origin-fallback.nxtrace.org" 196 } else { 197 // 默认配置完成,开始寻找最优 IP 198 fastIp = util.GetFastIP(host, port, true) 199 } 200 jwtToken, ua := envToken, []string{"Privileged Client"} 201 err := error(nil) 202 if envToken == "" { 203 if util.GetPowProvider() == "" { 204 jwtToken, err = pow.GetToken(fastIp, host, port) 205 } else { 206 jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port) 207 } 208 if err != nil { 209 log.Println(err) 210 os.Exit(1) 211 } 212 ua = []string{util.UserAgent} 213 } 214 cacheToken = jwtToken 215 cacheTokenFailedTimes = 0 216 requestHeader := http.Header{ 217 "Host": []string{host}, 218 "User-Agent": ua, 219 "Authorization": []string{"Bearer " + jwtToken}, 220 } 221 dialer := websocket.DefaultDialer 222 dialer.TLSClientConfig = &tls.Config{ 223 ServerName: host, 224 } 225 if proxyUrl != nil { 226 dialer.Proxy = http.ProxyURL(proxyUrl) 227 } 228 u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"} 229 // log.Printf("connecting to %s", u.String()) 230 231 c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader) 232 233 wsconn = &WsConn{ 234 Conn: c, 235 Connected: true, 236 Connecting: false, 237 MsgSendCh: make(chan string, 10), 238 MsgReceiveCh: make(chan string, 10), 239 } 240 241 if err != nil { 242 log.Println("dial:", err) 243 // <-time.After(time.Second * 1) 244 wsconn.Connected = false 245 wsconn.Done = make(chan struct{}) 246 go wsconn.keepAlive() 247 go wsconn.messageSendHandler() 248 return wsconn 249 } 250 // defer c.Close() 251 // 将连接写入WsConn,方便随时可取 252 wsconn.Done = make(chan struct{}) 253 go wsconn.keepAlive() 254 go wsconn.messageReceiveHandler() 255 go wsconn.messageSendHandler() 256 return wsconn 257 } 258 259 func New() *WsConn { 260 return createWsConn() 261 } 262 263 func GetWsConn() *WsConn { 264 return wsconn 265 }