Go语法入门 + 项目实战

👂 Take me Hand Acoustic - Cécile Corbel - 单曲 - 网易云音乐

第3个小项目有问题,不能在Windows下跑,懒得去搜Linux上怎么跑了,已经落下进度了....

目录

😳前言

🍉Go两小时

🔑小项目实战

🐘猜数字游戏

🐘在线词典

代码1  --  抓包

代码2  --  生成request body 

代码3  --  解析 response body

代码4  --  打印结果

代码6  --  完整代码

cmd测试

🐘Socks5代理服务器

🔑介绍

📚原理

💻TCP echo server

🚢auth

🏘请求阶段

🤪relay阶段

 📚Hertz框架


😳前言

注意!run时,要点func main()左边的绿色▲,而不是右上方的虫子和▲

注意2~ 一个文件夹下,只能有一个main.go文件,比如这样👇

学Go,除了字节内部课,还需要一套B站全流程的新手入门视频,只是内部课的话,节奏比较快,虽然也是有视频事无巨细地教,但有些点小白还是不懂的,需要到B站新手视频处查阅,需要一遍又一遍bing,Google,百度,GPT,stackOverFlow,youtube,B站(一共7个工具,记好了,加上算法群,项目群,在职群....效率不会低的)

还需要很多文档...

🍉Go两小时

注意先安装Goland,Git,弄个Github学生认证,免费使用

跟着敲一遍(182行代码),先有个印象👇

从 Java 的角度初识 Go 语言 | 青训营笔记 - HikariLan's Blog (minecraft.kim)

跟着敲完180行入门代码还不过瘾的话,再跟👇这个5小时教程,巩固一下基础

【golang教学】第二章:golang的基础知识——结构,包,变量初探(1010工作室出品)_哔哩哔哩_bilibili

代码 -- 复制可运行

package mainimport ("errors""fmt""time"
)import (_ "errors"
)// 错误处理
func findUser(users []user, name string) (v *user, err error) {for _, u := range users {if u.name == name {return &u, nil}}return nil, errors.New("not found")
}func add(a int, b int) int {return a + b //函数要写在主函数外
}func add2(n int) {n += 2
}func add2ptr(n *int) {*n += 2
}type user struct {name     stringpassword string
}// 检查用户密码是否匹配
func (u user) checkPassword(password string) bool {return u.password == password
}// 充值密码, 指针结构体
func (u *user) resetPassword(password string) {u.password = password
}func main() {var a int = 1var b, c int = 1, 3fmt.Println(a, b, c)if 7%2 == 0 {fmt.Println("7 is even")} else {fmt.Println("7 is odd")}t := 2 //声明并初始化一个新的变量switch t {case 0, 1:fmt.Println("zero or one")case 2:fmt.Println("two")default:fmt.Println("other")}tt := time.Now()switch {case tt.Hour() < 12:fmt.Println("it is before noon")default:fmt.Println("it is after noon")}v := 42switch v {case 100:fmt.Println(100)fallthroughcase 42:fmt.Println(42)fallthroughcase 1:fmt.Println(1)fallthroughdefault:fmt.Println("default")}for j := 7; j < 9; j++ {fmt.Println(j)}i := 1for i <= 3 {fmt.Println(i)i = i + 1}nums := []int{2, 3, 4}sum := 0for idx, num := range nums {fmt.Println("range to index", idx)sum += num}fmt.Println("sum:", sum)//遍历map, 得到 键 k 值 vm := make(map[string]int)m["hello"] = 0m["world"] = 1//if key and value both neededfor k, v := range m {fmt.Printf("%v %v\n", k, v)}//or only need keyfor k := range m {fmt.Printf("%v \n", k)}var aa [5]intfor i := 0; i < 3; i++ {aa[i] = ifmt.Println(aa[i])}bb := [5]int{1, 2, 3, 4, 5}fmt.Println(bb[4])var twoD [2][3]intfmt.Println(twoD[1][2])//切片s := make([]string, 3, 10)s[0] = "a"s[1] = "b"s[2] = "c"for i := 0; i < 3; i++ {fmt.Println(s[i])}//映射mm := map[string]int{"one": 111, "two": 2}fmt.Println(len(mm), mm["one"], mm["wtf"])fmt.Println(add(10, 2))n := 5add2(n)        //not workingfmt.Println(n) // 5add2ptr(&n)    //workingfmt.Println(n) // 7//初始化结构体cc := user{name: "wang", password: "1024"}fmt.Printf("%+v\n", cc) //{name:wang password:1024}fmt.Println(cc.name)fmt.Println(cc.password)//调用密码匹配结构体函数var dd userdd.resetPassword("2048")fmt.Println(dd.checkPassword("2048")) //true//调用错误函数u, err := findUser([]user{{"wang", "1024"}}, "wang")if err != nil {fmt.Println(err)return}fmt.Println(u.name)if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {fmt.Println(err) //not foundreturn} else {fmt.Println(u.name)}
}

🔑小项目实战

🐘猜数字游戏

描述

猜数字,猜大猜小了,都会有提示,知道最终猜中,结束循环

如果输入不合法,字符串不能转化为整型,就会提示Invalid Input

代码

// 数字猜测游戏
package mainimport ( //导入第3方包"bufio""fmt"_ "fmt" //format包, 输入输出字符串"math/rand"_ "math/rand""os""strconv"_ "strconv""strings""time"_ "time"
)func main() {maxNum := 100//Goland 1.15后不需要随机数种子, 会自动调用math/rand包的函数rand.Seed(time.Now().UnixNano())secretNumber := rand.Intn(maxNum) //生成0~100随机数//fmt.Println("The secret number is ", secretNumber)fmt.Println("Please input your guess")reader := bufio.NewReader(os.Stdin) //os.Stdin得到Stdin文件, bufio.NewReader转成只读的流for {input, err := reader.ReadString('\n') //ReadString读入一行输入if err != nil {fmt.Println("An error occured while reading input. Please try again", err)continue}input = strings.TrimSuffix(input, "\r\n") //strings包的TrimSuffix函数去掉回车符和换行符guess, err := strconv.Atoi(input) //Atoi转数字if err != nil {fmt.Println("Invalid input. Please enter an integer value")continue}fmt.Println("Your guess is", guess)if guess > secretNumber {fmt.Println("Your guess is bigger than the secret number. Please try again")} else if guess < secretNumber {fmt.Println("Your guess is smaller than the secret number. Please try again")} else {fmt.Println("Correct, you Legend")break}}
}

cmd测试

🐘在线词典

描述

先说下几个代码生成的工具(网址)👇
(1)👇Convert curl commands to Go (curlconverter.com)

用于将 cURL 命令转换为 Go 代码(也可以是其他语言)

cURL 是一个常用的命令行工具,用于发送 HTTP 请求并获取响应

将 cURL 命令转换为相应的编程语言代码,可以方便地在代码中,执行相同的 HTTP 请求

(2)👇JSON转Golang Struct - 在线工具 - OKTools

这个更好用,支持20多种转换

彩云小译 - 在线翻译 (caiyunapp.com)

👆上面网址打开,开发者工具

右键name,Copy,Copy as Curl(bash)粘贴到下面网址👇 就转化成Go代码了(或者其他语言)

Convert curl commands to code

(1)用Go语言发送http请求,解析json

代码1  --  抓包

输出一长串 json 字符

package mainimport ("fmt""io""log""net/http""strings"
)func main() {client := &http.Client{}//字符串data转成流var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)//1.创建请求//创建http的POST请求, 第二个参数是urlreq, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)if err != nil {log.Fatal(err)}//2.设置请求头req.Header.Set("authority", "api.interpreter.caiyunai.com")req.Header.Set("accept", "application/json, text/plain, */*")req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")req.Header.Set("app-name", "xy")req.Header.Set("content-type", "application/json;charset=UTF-8")req.Header.Set("device-id", "6febce1e00ffc1f9e33ddd0354386064")req.Header.Set("origin", "https://fanyi.caiyunapp.com")req.Header.Set("os-type", "web")req.Header.Set("os-version", "")req.Header.Set("referer", "https://fanyi.caiyunapp.com/")req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)req.Header.Set("sec-ch-ua-mobile", "?0")req.Header.Set("sec-ch-ua-platform", `"Windows"`)req.Header.Set("sec-fetch-dest", "empty")req.Header.Set("sec-fetch-mode", "cors")req.Header.Set("sec-fetch-site", "cross-site")req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183")req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")//3.发起请求resp, err := client.Do(req)if err != nil {log.Fatal(err) //请求失败,退出进程}defer resp.Body.Close() //拿到请求后,通过defer关闭流bodyText, err := io.ReadAll(resp.Body)if err != nil {log.Fatal(err)}fmt.Printf("%s\n", bodyText) //打印json字符串
}

👇由于最终要通过变量输入,而不是 json 字符串输入,所以需要 json 实例化

代码2  --  生成request body 

实例化一个 json,需要构造一个结构体,使结构体的名字和 json 的结构一一对应,再调用

json.Marshal()函数,举例👇

package mainimport ("encoding/json""fmt"
)type userInfo struct {Name stringAge intHobby []string
}func main() {a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}buf, err := json.Marshal(a)//...buf, err = json.MarshalIndent(a, "", "\t")...
}

修改后的代码

package mainimport ("bytes""encoding/json""fmt""io""log""net/http"
)// 构造结构体
type DictRequest struct {TransType string `json:"trans_type"` //翻译Source    string `json:"source"`     //原文本UserID    string `json:"user_id"`    //用户ID
}func main() {client := &http.Client{}//new一个结构体变量并初始化字段名request := DictRequest{TransType: "en2zh", Source: "good"}buf, err := json.Marshal(request) //json.Marshal实例化request//request转bytes数组var data = bytes.NewReader(buf)//1.创建请求//创建http的POST请求, 第二个参数是urlreq, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)if err != nil {log.Fatal(err)}//2.设置请求头req.Header.Set("authority", "api.interpreter.caiyunai.com")req.Header.Set("accept", "application/json, text/plain, */*")req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")req.Header.Set("app-name", "xy")req.Header.Set("content-type", "application/json;charset=UTF-8")req.Header.Set("device-id", "6febce1e00ffc1f9e33ddd0354386064")req.Header.Set("origin", "https://fanyi.caiyunapp.com")req.Header.Set("os-type", "web")req.Header.Set("os-version", "")req.Header.Set("referer", "https://fanyi.caiyunapp.com/")req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)req.Header.Set("sec-ch-ua-mobile", "?0")req.Header.Set("sec-ch-ua-platform", `"Windows"`)req.Header.Set("sec-fetch-dest", "empty")req.Header.Set("sec-fetch-mode", "cors")req.Header.Set("sec-fetch-site", "cross-site")req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183")req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")//3.发起请求resp, err := client.Do(req)if err != nil {log.Fatal(err) //请求失败,退出进程}defer resp.Body.Close() //拿到请求后,通过defer关闭流bodyText, err := io.ReadAll(resp.Body)if err != nil {log.Fatal(err)}fmt.Printf("%s\n", bodyText) //打印json字符串
}

代码3  --  解析 response body

将一大串 response 解析,获取里面的几个字段

按 request 的方式,构造结构体,结构体字段和返回的 response 一一对应,再将返回的 json 字符串反序列化到结构体里


但是,这里的字段非常复杂👆,这时需要一个 json 转Go Struct 的工具👇

JSON转Golang Struct - 在线工具 - OKTools

整块代码粘贴过去

右键copy value,转换-嵌套

type DictResponse struct {Rc   int `json:"rc"`Wiki struct {} `json:"wiki"`Dictionary struct {Prons struct {EnUs string `json:"en-us"`En   string `json:"en"`} `json:"prons"`Explanations []string      `json:"explanations"`Synonym      []string      `json:"synonym"`Antonym      []string      `json:"antonym"`WqxExample   [][]string    `json:"wqx_example"`Entry        string        `json:"entry"`Type         string        `json:"type"`Related      []interface{} `json:"related"`Source       string        `json:"source"`} `json:"dictionary"`
}

代码4  --  打印结果

var dictResponse DictResponse //定义DictResponse变量//json.Unmarshal将bodyText解析为结构体变量的值err = json.Unmarshal(bodyText, &dictResponse) //& 写入结构体if err != nil {log.Fatal(err)}fmt.Printf("%#v\n", dictResponse) //%#v 打印结构体 详细值

输出

但是我们只需要 音标(prons) 和 解释(explanations)

代码5  --  完善结果

代码6  --  完整代码

package mainimport ("bytes""encoding/json""fmt""io""log""net/http""os"
)// 构造结构体
type DictRequest struct {TransType string `json:"trans_type"` //翻译Source    string `json:"source"`     //原文本UserID    string `json:"user_id"`    //用户ID
}// 结构体嵌套
type DictResponse struct {Rc   int `json:"rc"`Wiki struct {} `json:"wiki"`Dictionary struct {Prons struct {EnUs string `json:"en-us"`En   string `json:"en"`} `json:"prons"`Explanations []string      `json:"explanations"`Synonym      []string      `json:"synonym"`Antonym      []string      `json:"antonym"`WqxExample   [][]string    `json:"wqx_example"`Entry        string        `json:"entry"`Type         string        `json:"type"`Related      []interface{} `json:"related"`Source       string        `json:"source"`} `json:"dictionary"`
}func query(word string) {client := &http.Client{}//new一个结构体变量并初始化字段名//request := DictRequest{TransType: "en2zh", Source: "good"} //固定字符串request := DictRequest{TransType: "en2zh", Source: word} //变量wordbuf, err := json.Marshal(request)                        //json.Marshal实例化request//request转bytes数组var data = bytes.NewReader(buf)//1.创建请求//创建http的POST请求, 第二个参数是urlreq, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)if err != nil {log.Fatal(err)}//2.设置请求头req.Header.Set("authority", "api.interpreter.caiyunai.com")req.Header.Set("accept", "application/json, text/plain, */*")req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")req.Header.Set("app-name", "xy")req.Header.Set("content-type", "application/json;charset=UTF-8")req.Header.Set("device-id", "6febce1e00ffc1f9e33ddd0354386064")req.Header.Set("origin", "https://fanyi.caiyunapp.com")req.Header.Set("os-type", "web")req.Header.Set("os-version", "")req.Header.Set("referer", "https://fanyi.caiyunapp.com/")req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)req.Header.Set("sec-ch-ua-mobile", "?0")req.Header.Set("sec-ch-ua-platform", `"Windows"`)req.Header.Set("sec-fetch-dest", "empty")req.Header.Set("sec-fetch-mode", "cors")req.Header.Set("sec-fetch-site", "cross-site")req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183")req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")//3.发起请求resp, err := client.Do(req)if err != nil {log.Fatal(err) //请求失败,退出进程}defer resp.Body.Close() //拿到请求后,通过defer关闭流bodyText, err := io.ReadAll(resp.Body)if err != nil {log.Fatal(err)}if resp.StatusCode != 200 { //记录错误信息并终止执行, 便于排查问题log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))}var dictResponse DictResponse //定义DictResponse变量//json.Unmarshal将bodyText解析为结构体变量的值err = json.Unmarshal(bodyText, &dictResponse) //& 写入结构体if err != nil {log.Fatal(err)}fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)for _, item := range dictResponse.Dictionary.Explanations {fmt.Println(item) //range遍历数组每个元素并打印}
}func main() {if len(os.Args) != 2 { //后面没有接一个单词,打印错误并退出fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello`)os.Exit(1)}word := os.Args[1] //后面接一个单词,打印单词query(word)
}

cmd测试

🐘Socks5代理服务器

🔑介绍

📚原理

SOCKS5代理服务器充当客户端和目标服务器之间的中介。它接收来自客户端的请求,并将其转发到目标服务器,然后将目标服务器的响应转发回客户端。这样,客户端可以通过代理服务器访问目标服务器上的资源,同时实现了客户端与目标服务器之间的隔离和隐藏👇

👆

  1. 客户端连接到SOCKS5代理服务器:客户端首先与SOCKS5代理服务器建立连接。这个连接是直接建立的,不会经过目标服务器

  2. 客户端发送代理请求:一旦与代理服务器建立连接,客户端就会向代理服务器发送代理请求。请求中包含要连接的目标服务器的地址、端口号和认证信息(如果需要)

  3. 代理服务器建立连接:代理服务器收到代理请求后,会解析其中的目标服务器地址和端口号,并尝试与目标服务器建立连接

  4. 代理服务器与目标服务器交换数据:一旦代理服务器成功地与目标服务器建立连接,它会在客户端和目标服务器之间进行数据中转。代理服务器接收来自客户端的数据,并将其发送给目标服务器;相应地,它从目标服务器接收数据,并将其发送回客户端

  5. 数据传输完成:当数据传输完成后,可以根据具体的需求来决定是否保持连接或断开连接

💻TCP echo server

Socks5代理服务器较为复杂,我们先从简化版本 TCP echo server开始

TCP回显服务器(TCP echo server)是一种基于TCP协议的网络服务器,它接收客户端发送的数据并将其原封不动地返回给客户端

不挣扎了,检索了2小时,都没跑通,最后群里一问,这玩意没法在Windows跑?!

所以,下面👇只是粘贴代码上去,不作解释和运行测试

package mainimport ("bufio""log""net"
)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)for {b, err := reader.ReadByte()if err != nil {break}_, err = conn.Write([]byte{b})if err != nil {break}}
}

🚢auth

...

package mainimport ("bufio""fmt""io""log""net"
)const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04func 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/PASSWORDver, 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)}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
}

🏘请求阶段

...

package mainimport ("bufio""encoding/binary""errors""fmt""io""log""net"
)const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04func 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/PASSWORDver, 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])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)}return nil
}

🤪relay阶段

...

package mainimport ("bufio""context""encoding/binary""errors""fmt""io""log""net"
)const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04func 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/PASSWORDver, 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])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)}ctx, cancel := context.WithCancel(context.Background())defer cancel()go func() {_, _ = io.Copy(dest, reader)cancel()}()go func() {_, _ = io.Copy(conn, dest)cancel()}()<-ctx.Done()return nil
}

 📚Hertz框架

资源

看一遍👇

一文学会 Go 的三个主流开发框架| 青训营笔记 - HikariLan's Blog (minecraft.kim)

李文周的👇

【置顶】Go语言学习之路/Go语言教程 | 李文周的博客 (liwenzhou.com)

Hertz中文教程👇

Hertz | CloudWeGo

Hertz(Github搭建安装学习全流程)👇

hertz/README_cn.md at develop · cloudwego/hertz (github.com)

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

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

相关文章

《Kubernetes故障篇:unable to retrieve OCI runtime error》

一、背景信息 1、环境信息如下&#xff1a; 操作系统K8S版本containerd版本Centos7.6v1.24.12v1.6.12 2、报错信息如下&#xff1a; Warning FailedCreatePodSandBox 106s (x39 over 10m) kubelet (combined from similar events): Failed to create pod sandbox: rpc error: …

【SAP Abap】记录一次SAP长文本内容通过Web页面完整显示的应用

【SAP Abap】记录一次SAP长文本内容通过Web页面完整显示的应用 1、业务背景2、实现效果3、开发代码3.1、拼接html3.2、显示html3.3、ALV导出Excel 1、业务背景 业务在销售订单中&#xff0c;通过长文本描述&#xff0c;记录了一些生产备注信息&#xff0c;如生产标准、客户要求…

CentOS7安装jenkins

一、安装相关依赖 sudo yum install -y wget sudo yum install -y fontconfig java-11-openjdk二、安装Jenkins 可以查看官网的安装方式 安装官网步骤 先导入jenkins yum 源 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo…

索引的数据结构

索引的数据结构 部分资料来自B站尚硅谷-宋红康老师 1. 为什么使用索引 使用索引是为了加快数据库的查询速度和提高数据库的性能。索引是数据库表中的一种数据结构&#xff0c;它可以帮助数据库快速定位并检索所需的数据。 当数据库表中的数据量较大时&#xff0c;如果没有索…

ELK + Fliebeat + Kafka日志系统

参考&#xff1a; ELKFilebeatKafka分布式日志管理平台搭建_51CTO博客_elk 搭建 ELK 日志分析系统概述及部署&#xff08;上&#xff09;-阿里云开发者社区 ELK是三个开源软件的缩写&#xff0c;分别表示&#xff1a;Elasticsearch , Logstash, Kibana , 它们都是开源软件。…

Verilog语法学习——LV6_多功能数据处理器

LV6_多功能数据处理器 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 根据指示信号select的不同&#xff0c;对输入信号a,b实现不同的运算。输入信号a…

解决使用@Field注解配置分词器失效问题(Spring Data Elasticsearch)

问题复现&#xff1a;插入数据时&#xff0c;实体类配置的Field注解没有生效 实体类&#xff1a; package cn.aopmin.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import…

浅谈深度神经网络

Deep neural networks are completely flexible by design, and there really are no fixed rules when it comes to model architecture. -- David Foster 前言 神经网络 (neural network) 受到人脑的启发&#xff0c;可模仿生物神经元相互传递信号。神经网络就是由神经元组成…

docker配置文件挂载(容器数据管理)

目录 数据卷&#xff08;容器数据管理&#xff09;什么是数据卷数据集操作命令创建和查看数据卷挂载数据卷案例案例-给nginx挂载数据卷案例-给MySQL挂载本地目录 总结 数据卷&#xff08;容器数据管理&#xff09; 在之前的nginx案例中&#xff0c;修改nginx的html页面时&#…

如何利用Requestly提升前端开发与测试的效率

痛点 B站最牛的Python接口自动化测试进阶教程合集&#xff08;真实企业项目实战&#xff09; 前端测试 在进行前端页面开发或者测试的时候&#xff0c;我们会遇到这一类场景&#xff1a; 在开发阶段&#xff0c;前端想通过调用真实的接口返回响应在开发或者生产阶段需要验证前…

热备盘激活失败导致raid5阵列崩溃的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 一台Linux Redhat操作系统服务器上有一组由5块硬盘组建的raid5阵列&#xff0c;包含一块热备盘。上层部署一个OA系统和Oracle数据库。 服务器故障&#xff1a; raid5阵列中的1块磁盘离线&#xff0c;硬盘离线却没有激活热备盘&#xff0c;直到…

IntelliJ IDEA 2023.2 主要更新了什么?(图文版)

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Spring 6【数据校验Validation、JSR 303 和 Hibernate 实现】(十三)-全面详解(学习总结---从入门到深化)

目录 数据校验Validation 2.JSR 303 和 Hibernate 实现 数据校验Validation 1.数据校验介绍 数据校验分为客户端数据校验和服务端数据校验。都是为了保证数据完整性的。 客户端数据校验直接使用客户端脚本校验即可。例如在B/S模型项目中&#xff0c;可以选择使用JavaScript…

CS144学习笔记(1):Internet and IP

1.网络应用 网络应用可以在世界范围内交换数据&#xff0c;例如你可以通过浏览器读取出版社服务器提供的文章。网络应用的基本模型&#xff1a;两台主机各自在本地运行一个程序&#xff0c;程序通过网络来通信。 最常用的通信模型使用双向可靠字节流&#xff0c;通信的两台主机…

实战:Docker+Jenkins+Gitee构建CICD流水线

文章目录 前言Jenkins部署创建Jenkins docker-compose配置maven源启动Jenkins容器安装插件Gitee ssh公匙配置与测试项目提交 Jenkins创建流水线写在最后 前言 持续集成和持续交付一直是当下流行的开发运维方式&#xff0c;CICD省去了大量的运维时间&#xff0c;也能够提高开发…

ElasticSearch基本使用--ElasticSearch文章一

文章目录 官网学习必要性elasticsearch/kibana安装版本数据结构说明7.x版本说明ElasticSearch kibana工具测试后续我们会一起分析 官网 https://www.elastic.co/cn/ 学习必要性 1、在当前软件行业中&#xff0c;搜索是一个软件系统或平台的基本功能&#xff0c; 学习Elastic…

Git克隆文件不显示绿色勾、红色感叹号等图标

1、问题 Git和TorToiseGit安装后&#xff0c;Git克隆的文件不会显示绿色勾、红色感叹号等图标。 2、检查注册表 2.1、打开注册表 (1)WinR打开运行窗口&#xff0c;输入regedit&#xff0c;点击确定&#xff0c;打开注册表编辑器。 2.2、找如下路径 (1)找到路径 计算机\HKEY_…

VBA技术资料MF34:检查Excel自动筛选是否打开

【分享成果&#xff0c;随喜正能量】聪明人&#xff0c;抬人不抬杠&#xff1b;傻子&#xff0c;抬杠不抬人。聪明人&#xff0c;把别人抬得很高&#xff0c;别人高兴、舒服了&#xff0c;看你顺眼了&#xff0c;自然就愿意帮你&#xff01;而傻人呢&#xff1f;不分青红皂白&a…

Golang之路---01 Golang的安装与配置

Golang之路—01 Golang语言安装与配置 官网上下载Windows环境下的安装包 官网下载地址 双击下载后的文件进行安装&#xff0c;可根据需要自定义选择解压后的文件位置。 接着新创建一个文件夹&#xff0c;保存Golang语言项目。 在里面新建bin,pkg,src三个文件夹。 环境变量…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…