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代理服务器充当客户端和目标服务器之间的中介。它接收来自客户端的请求,并将其转发到目标服务器,然后将目标服务器的响应转发回客户端。这样,客户端可以通过代理服务器访问目标服务器上的资源,同时实现了客户端与目标服务器之间的隔离和隐藏👇
👆
客户端连接到SOCKS5代理服务器:客户端首先与SOCKS5代理服务器建立连接。这个连接是直接建立的,不会经过目标服务器
客户端发送代理请求:一旦与代理服务器建立连接,客户端就会向代理服务器发送代理请求。请求中包含要连接的目标服务器的地址、端口号和认证信息(如果需要)
代理服务器建立连接:代理服务器收到代理请求后,会解析其中的目标服务器地址和端口号,并尝试与目标服务器建立连接
代理服务器与目标服务器交换数据:一旦代理服务器成功地与目标服务器建立连接,它会在客户端和目标服务器之间进行数据中转。代理服务器接收来自客户端的数据,并将其发送给目标服务器;相应地,它从目标服务器接收数据,并将其发送回客户端
数据传输完成:当数据传输完成后,可以根据具体的需求来决定是否保持连接或断开连接
💻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)
相关文章:
![](https://img-blog.csdnimg.cn/75b4c97ace994194b5aeee758c0826b7.png)
Go语法入门 + 项目实战
👂 Take me Hand Acoustic - Ccile Corbel - 单曲 - 网易云音乐 第3个小项目有问题,不能在Windows下跑,懒得去搜Linux上怎么跑了,已经落下进度了.... 目录 😳前言 🍉Go两小时 🔑小项目实战 …...
![](https://img-blog.csdnimg.cn/80724548659f434781ba0d66149cb12b.png)
QT控件通过qss设置子控件的对齐方式、大小自适应等
一些复杂控件,是有子控件的,每个子控件,都可以通过qss的双冒号选择器来选中,进行独特的样式定义。很多控件都有子控件,太多了,后面单独写一篇文章来介绍各个控件的子控件。这里就随便来几个例子 例如下拉列…...
![](https://www.ngui.cc/images/no-images.jpg)
基于java在线收银系统设计与实现
摘要 科技的力量总是在关键的地方改变着人们的生活,不仅如此,我们的生活也是离不开这样或者那样的科技改变,有的消费者没有时间去商场购物,那么电商和快递的结合让端口到消费者的距离不再遥远;有的房客因地域或者工作的…...
![](https://img-blog.csdnimg.cn/3e60187b001c42809074ae3753a33a4d.png)
Linux--进程的新建状态
新建状态: 操作系统创建了进程的内核数据结构(task_struct、mm_struct、页表),但是页表没有创建映射关系,而且磁盘里的程序的代码和数据未加载到物理内存...
![](https://www.ngui.cc/images/no-images.jpg)
区间dp,合并石子模板题
设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的…...
![](https://img-blog.csdnimg.cn/b525ae77542c4de098fb389a3398d62a.png)
C++代码格式化工具clang-format详细介绍
文章目录 clang-format思考代码风格指南生成您的配置运行 clang-format禁用一段代码的格式设置clang-format的设置预览 clang-format 我曾在许多编程团队工作过,这些团队名义上都有“编程风格指南”。该指南经常被写下来并放置在开发人员很少查看的地方。几乎在每种…...
![](https://img-blog.csdnimg.cn/img_convert/5966026c45f1867860814ab893439203.png)
CentOS 7安装PostgreSQL 15版本数据库
目录 一、何为PostgreSQL? 二、PostgreSQL安装 2.1安装依赖 2.2 执行安装 2.3 数据库初始化 2.4 配置环境变量 2.5 创建数据库 2.6 配置远程 2.7 测试远程 三、常用命令 四、用户创建和数据库权限 一、何为PostgreSQL? PostgreSQL是以加州大学…...
![](https://img-blog.csdnimg.cn/c9f5c46fd342458289df1ca8e6fbba65.png#pic_center)
QGraphicsView实现简易地图2『瓦片经纬度』
前文链接:QGraphicsView实现简易地图1『加载离线瓦片地图』 地图采用GCJ02 Web 墨卡托投影,最小坐标:(-180.00000000000000,-85.05112877980655),最大坐标:(180.00000000000000,85.05112877980655)。瓦片地图单张图片像…...
![](https://www.ngui.cc/images/no-images.jpg)
医学图像重建—第一章笔记
序言 本书涵盖内容: 2D parallel beam imaging 2D fan beam imaging 3D parallel ray imaging 3D parallel plane imaging 3D cone beam imaging 算法包括:analytical method,iterative method 应用于: X-ray CT single photon…...
![](https://www.ngui.cc/images/no-images.jpg)
python-pytorch基础之神经网络分类
这里写目录标题 生成数据函数定义数据集定义loader加载数据定义神经网络模型测试输出是否为2个输入数据,输出结果 训练模型函数计算正确率 训练数据并保存模型测试模型准备数据加载模型预测对比结果 生成数据函数 import randomdef get_rectangle():widthrandom.ra…...
![](https://img-blog.csdnimg.cn/a7883763657f418ebfcdba3060461461.png)
【C++ 程序设计】实战:C++ 变量实践练习题
目录 01. 变量:定义 02. 变量:初始化 03. 变量:参数传递 04. 变量:格式说明符 ① 占位符 “%d” 改为格式说明符 “%llu” ② 占位符 “%d” 改为格式说明符 “%f” 或 “%e” 05. 变量:字节数统计 06. 变量&a…...
![](https://img-blog.csdnimg.cn/c90ded869c5d44b383189e2dee05f552.png#pic_center)
微软对Visual Studio 17.7 Preview 4进行版本更新,新插件管理器亮相
近期微软发布了Visual Studio 17.7 Preview 4版本,而在这个版本当中,全新设计的扩展插件管理器将亮相,并且可以让用户可更简单地安装和管理扩展插件。 据了解,目前用户可以从 Visual Studio Marketplace 下载各式各样的 VS 扩展插…...
![](https://img-blog.csdnimg.cn/493d3d469f1341edb94d1d4e7ce78fba.png)
Kafka 入门到起飞 - Kafka怎么做到保障消息不会重复消费的? 消费者组是什么?
Kafka怎么做到避免消息重复消费的? 消费者组是什么? 消费者: 1、订阅Topic(主题) 2、从订阅的Topic消费(pull)消息, 3、将消费消息的offset(偏移量)保存在K…...
![](https://www.ngui.cc/images/no-images.jpg)
MongoDB 的增、查、改、删
Monogo使用 增 单条增加 db.member.insertOne({"name":"张三","age":18,"create":new Date()}) db.member.insert({"name":"李四1","age":18,"create":new Date()}) db.member.insertOne(…...
![](https://www.ngui.cc/images/no-images.jpg)
mysql常用操作命令
mysql常用操作命令 mysql:单进程多线程模型,一个SQL语句无法利用多个cpu core 一:基本命令 0.查看当前连接数 show global status like Thread$; show variables like "%timeout%"; show variables like "log_%";1.查看当前连接状态 show processlist…...
![](https://www.ngui.cc/images/no-images.jpg)
数学建模常见模型汇总
优化问题 线性规划、半定规划、几何规划、非线性规划、整数规划、多目标规划(分层序列法)、动态规划、存贮论、代理模型、响应面分析法、列生成算法 预测模型 微分方程、小波分析、回归分析、灰色预测、马尔可夫预测、时间序列分析(AR MAMA.RMA ARTMA LSTM神经网络)、混沌模…...
![](https://www.ngui.cc/images/no-images.jpg)
C#使用LINQ查询操作符实例代码(二)
目录 六、连表操作符 1、内连接2、左外连接(DefaultIfEmpty)3、组连接七、集合操作 八、分区操作符 1、Take():2、TakeWhile():3、Skip():4、SkipWhile():九、聚合操作符 1、Count: 返回集合项数。 2、LongCount&…...
![](https://www.ngui.cc/images/no-images.jpg)
jenkinsfile小试牛刀
序 本文主要演示一下如何用jenkinsfile来编译java服务 安装jenkins 这里使用docker来安装jenkins docker run --name jenkins-docker \ --volume $HOME/jenkins_home:/var/jenkins_home \ -p 8080:8080 jenkins/jenkins:2.416之后访问http://${yourip}:8080,然后…...
![](https://www.ngui.cc/images/no-images.jpg)
C++ xmake构建
文章目录 一、xmake.lua二、xmake常用语句 一、xmake.lua --xmake.luaset_project("XXX")add_rules("mode.debug", "mode.release") set_config("arch", "x64")if is_plat("windows") then -- the release modei…...
![](https://img-blog.csdnimg.cn/60effa480fda42e08d0763b79d3d78ba.png)
推荐带500创作模型的付费创作V2.1.0独立版系统源码
ChatGPT 付费创作系统 V2.1.0 提供最新的对应版本小程序端,上一版本增加了 PC 端绘画功能, 绘画功能采用其他绘画接口 – 意间 AI,本版新增了百度文心一言接口。 后台一些小细节的优化及一些小 BUG 的处理,前端进行了些小细节优…...
![](https://img-blog.csdnimg.cn/6d3c404e6f66427a9c36c5c2476131d2.png)
wps图表怎么改横纵坐标,MLP 多层感知器和CNN卷积神经网络区别
目录 wps表格横纵坐标轴怎么设置? MLP (Multilayer Perceptron) 多层感知器 CNN (Convolutional Neural Network) 卷积神经网络 多层感知器MLP,全连接网络,DNN三者的关系 wps表格横纵坐标轴怎么设置? 1、打开表格点击图的右侧…...
![](https://www.ngui.cc/images/no-images.jpg)
rdb和aof
RDB持久化:原理是将Redis在内存中的数据库记录定时dump到磁盘上的RDB持久化AOF持久化:原理是将Redis的操作日志以追加的方式写入文件 rdb: 开启方式:客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件&#…...
![](https://img-blog.csdnimg.cn/d723762109cd4c5ab96038f1a91fa6ae.png)
TCP网络通信编程之网络上传文件
【图片】 【思路解析】 【客户端代码】 import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException;/*** ProjectName: Study* FileName: TCPFileUploadClient* author:HWJ* Data: 2023/7/29 18:44*/ public class TCPFil…...
![](https://img-blog.csdnimg.cn/a9baeb1dd45f4dc58fb8927095f634f9.png#pic_center)
Java中对Redis的常用操作
目录 数据类型五种常用数据类型介绍各种数据类型特点 常用命令字符串操作命令哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令 在Java中操作RedisRedis的Java客户端Spring Data Redis使用方式介绍环境搭建配置Redis数据源编写配置类,创建RedisTempla…...
![](https://img-blog.csdnimg.cn/d424345310cc479193490e2ef87f86b3.png)
链路追踪设计
...
![](https://img-blog.csdnimg.cn/74ba64dd71ef4484b77c2b064ec90d04.png)
Golang之路---02 基础语法——常量 (包括特殊常量iota)
常量 //显式类型定义const a string "test" //隐式类型定义const b 20 //多个常量定义 const(c "test2"d 2.3e 27)iota iota是Golang语言的常量计数器,只能在常量表达式中使用 iota在const关键字出现时将被重置为0,const中每新…...
![](https://img-blog.csdnimg.cn/abda4d3ea0da4b7f832ff23645f2e0f2.png)
Pytest学习教程_装饰器(二)
前言 pytest装饰器是在使用 pytest 测试框架时用于扩展测试功能的特殊注解或修饰符。使用装饰器可以为测试函数提供额外的功能或行为。 以下是 pytest 装饰器的一些常见用法和用途: 装饰器作用pytest.fixture用于定义测试用例的前置条件和后置操作。可以创建可重…...
![](https://img-blog.csdnimg.cn/0e948fe981364439ba7a9372304f2c86.png)
redis的如何使用
1、redis的使用 1.1windows安装 安装包下载地址:Releases dmajkic/redis GitHub 1.2 redis中常使用的几个文件 1.3 redis中运行 双击redis-server,既可以运行。 1.4使用redis客户单来连接redis 1.5redis的常用指标 redis-serve 服务端,端口号&am…...
![](https://img-blog.csdnimg.cn/6239389f3a4141eb9d92c46837634f42.png)
MyBatis(二)
文章目录 一.MyBatis的模式开发1.1 定义数据表和实体类1.2 配置数据源和MyBatis1.3 编写Mapper接口和增加xxxMapper.xml1.4 测试我们功能的是否实现. 二. Mybatis的增删查改操作2.1 单表查询2.2 多表查询三.动态SQL的实现3.1 什么是动态SQL3.2 动态SQL的使用if标签的使用trim标…...
![](https://img-blog.csdnimg.cn/c5c62b6f7e514c0188646895c2f88dd0.png)
【【51单片机AD转换模块】】
代码是简单的,板子是坏的,电阻是识别不出来的 main.c #include <REGX52.H> #include "delay.h" #include "LCD1602.h" #include "XPT2046.h"unsigned int ADValue;void main(void) {LCD_Init();LCD_ShowString(1,1…...
![](/images/no-images.jpg)
wordpress 英文/宁波seo关键词优化方法
敏捷领导力总结一下:您的敏捷转换被困住了。 您已经考虑了自己的原因,就像成为敏捷领导者一样,第1部分:定义原因 。 您已经开始衡量可能性。 您像要成为敏捷领导者,第2部分:与谁接触一样,对与谁…...
![](/images/no-images.jpg)
手机网站的制作/铜仁搜狗推广
JavaScript的变量是松散类型的,即可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用var操作符,后跟变量名,如下:var test;这行代码定义了一个名为test的变量…...
![](https://img-blog.csdnimg.cn/img_convert/4f6117b192327eee462a99c772032bed.png)
wordpress标签页插件/营销策划思路及方案
Android 7.0 锁屏解锁之向上滑动显示解锁界面分析by jing.chen锁屏的解锁操作是在锁屏界面向上滑动实现的,通过向上滑动调出解锁界面(如图案、PIN、密码解锁界面),在解锁界面输入正确的密码之后解锁显示launcher。向上滑动如何调出解锁界面,需…...
![](/images/no-images.jpg)
网站开发背景400字/营销网络图
/* 设置uitabr 样式 此方法需要设置了viewControllers 后设置了后调用 tabBar.viewControllers [NSArray arrayWithObjects:nav1,nav2,nav3,nil]; */ -(void) setUITabBarStyle :(UITabBarController *)tabBar { for(UIView *view in tabBar.tabBar.subviews) { if([vie…...
![](/images/no-images.jpg)
做网站订单/推广普通话手抄报内容简短
OSD进程的启动停止:https://blog.csdn.net/bandaoyu/article/details/119894927 1. OSD概念 OSD:Object Storage Device,主要负责响应客户端请求返回具体数据的守护进程,一般一个集群会有多个OSD,每一块盘都会对应一个…...
![](/images/no-images.jpg)
湖南建设人力资源网和报考平台/齐三seo顾问
01 开启多条线程,每条线程都只下载文件的一部分(通过设置请求头中的Range来实现) 02 创建一个和需要下载文件大小一致的文件,判断当前是那个线程,根据当前的线程来判断下载的数据应该写入到文件中的哪个位置。…...