这是我参与「第五届青训营 」笔记创作活动的第1天。
课堂讲授内容
- Go 语言基础知识,变量、控制结构、函数、字符串、分片、json编组和解码等;
- 一些库的相关介绍和实践,如fmt、time、math/rand等;
- 通过三个小项目实践介绍和巩固 Go 相关知识点,更多地集中在网络开发,顺带控制台输入、流、随机数生成等。
什么是 Go 语言?
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
基础语法
-
变量声明和赋值(两种方式)
a.
var 变量名 数据类型,如 var i int; b.:=,如 s := “Hello world.” -
switch 分支:(和C/C++不同)不需要 break,执行完一个分支,直接转到最后。
-
切片slice
使用
make创建切片,其他操作如append,:切片等package main import "fmt" func main() { arr := make([]int, 3) arr[0] = 0 arr[1] = 1 arr[2] = 2 fmt.Println("arr:", arr) arr = append(arr, 4) fmt.Println("arr:", arr) arrCopy := make([]int, len(arr)) copy(arrCopy, arr) fmt.Println("arrCopy:", arrCopy) } -
字符串处理
package main import ( "fmt" "strings" ) func main() { a := "hello" fmt.Println(strings.Contains(a, "ll")) // true fmt.Println(strings.Count(a, "l")) // 2 fmt.Println(strings.HasPrefix(a, "he")) // true fmt.Println(strings.HasSuffix(a, "llo")) // true fmt.Println(strings.Index(a, "ll")) // 2 fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo fmt.Println(strings.Repeat(a, 2)) // hellohello fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo fmt.Println(strings.Split("a-b-c", "-")) // [a b c] fmt.Println(strings.ToLower(a)) // hello fmt.Println(strings.ToUpper(a)) // HELLO fmt.Println(len(a)) // 5 b := "你好" fmt.Println(len(b)) // 6 } -
json处理:
json.Marshal()序列化,json.MarshalIndent(a, "", "\t"),json.Unmarshal(buf, &b)反序列化package main import ( "encoding/json" "fmt" ) type userInfo struct { Name string Age int `json:"age"` Hobby []string } func main() { a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}} buf, err := json.Marshal(a) if err != nil { panic(err) } fmt.Println(buf) // [123 34 78 97...] fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]} buf, err = json.MarshalIndent(a, "", "\t") if err != nil { panic(err) } fmt.Println(string(buf)) var b userInfo err = json.Unmarshal(buf, &b) if err != nil { panic(err) } fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}} }
Go 语言实战案例
猜数字游戏
-
涉及知识:随机数(种子)、控制台输入
a. 随机数(种子):随机数种子能够保证每次生成的随机数不一样。常用时间戳
time.Now().UnixNano()做种子。例:maxNum := 100 rand.Seed(time.Now().UnixNano()) // 设置随机数种子,保证每次生成的随机数不一样 secretNumber := rand.Intn(maxNum)b. 控制台输入:
bufio.NewReader(os.Stdin)和ReadString -
完整示例:给指定长度的数组赋随机值,并输出。
package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) func main() { fmt.Println("Please input length of array") reader := bufio.NewReader(os.Stdin) input, err := reader.ReadString('\n') if err != nil { fmt.Println("Error.") return } // fmt.Println(input) input = strings.Trim(input, "\r\n") n, err := strconv.Atoi(input) if err != nil { fmt.Println("Error.") return } var arr = make([]int, n) for i := 0; i < n; i++ { rand.Seed(time.Now().UnixNano()) arr[i] = rand.Intn(100) } fmt.Println(arr) }
简单在线词典
-
涉及知识点:网络传输、抓包、json 编组和解码
a. 网络传输和抓包:Convert curl to Go
- (demo项目)创建请求、发送请求和接受响应请求:用彩云小译进行抓包,“右击检查→点击翻译时在 Name 处找到 Request Method 为 POST 的 dict→右击 copy cURL(bash) 到Convert curl to Go 进行转换为 Go”
b. json 编组和解码:json 转 Golang struct
-
(demo项目)“复制彩云小译检查中的 Response 中的内容到 json 转 Golang struct 进行 Go 结构体转换”
-
json 编组和解码
-
json 编组:
json.Marshal(v)orjson.MarshalIndent(v, prefix, indent),将 Go 数据结构转化为 json 格式。package main import ( "encoding/json" "fmt" "log" ) type Movie struct { Title string Year int `json:"released"` Color bool `json:"color,omitempty"` Actors []string } func main() { var movies = []Movie{ {Title: "Casablanca", Year: 1942, Color: false, Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}}, {Title: "Cool Hand Luke", Year: 1967, Color: true, Actors: []string{"Paul Newman"}}, {Title: "Bullitt", Year: 1968, Color: true, Actors: []string{"Steve McQueen", "Jacqueline Bisset"}}, // ... } // 1.无格式化显示 //data, err := json.Marshal(movies) //if err != nil { // log.Fatalf("JSON marshaling failed: %s", err) //} //fmt.Printf("%s\n", data) // 2.格式化显示 data, err := json.MarshalIndent(movies, "", " ") if err != nil { log.Fatalf("JSON marshaling failed: %s", err) } fmt.Printf("%s\n", data) } /* 输出 1.无格式化 [{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}] 2.格式化 [ { "Title": "Casablanca", "color": true, "Actors": [ "Steve McQueen", "Jacqueline Bisset" ] } ] "released": 1967, "color": true, "Actors": [ "Paul Newman" ] }, { "Title": "Bullitt", "color": true, "Actors": [ "Steve McQueen", ] "Jacqueline Bisset" } ] */- json 解码:
json.Unmarshal()将 json 格式转化为 Go 数据结构
接上例
var titles []struct{ Title string } if err := json.Unmarshal(data, &titles); err != nil { log.Fatalf("JSON unmarshaling failed: %s", err) } fmt.Println(titles)- json TAG:
omitempty表示当Go语言结构体成员为空或零值时不生成该JSON对象(这里false为零值)
- json 解码:
-
SOCKS5 代理
原理
-
步骤1:认证阶段 auth
$ go run main.go$ curl --socks5 127.0.0.1:1080 -v [<http://www.qq.com>](<http://www.qq.com/>)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/PASSWORD ... -
请求阶段
请求:ver、cmd、atyp
响应:用到的不多
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个字节 ... // +----+-----+-------+------+----------+----------+ // |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 ... } -
relay 阶段
两个 go routing 中
io.Copy()相反,第一个是从用户传到底层服务器,第二个相反。func connect(reader *bufio.Reader, conn net.Conn) (err error) { ... go func() { _, _ = io.Copy(dest, reader) cancel() }() go func() { _, _ = io.Copy(conn, dest) cancel() }() <-ctx.Done() ... }
其他参考资料
课后总结
- 收获:第一次接触 Go 语言,感觉这是一个功能很强大的语言,带有丰富的库和接口能够简单地完成很多复杂的操作,对于并发的设计也非常简单,在诸多领域起着非常高的开发效率,老师的讲解大部分是能够理解的,配套的实践项目也巩固了自己的语言学习。
- 疑难:对于 go routing 和并发设计的理解还不够深刻,以及一些网络知识有点模糊,争取课下继续深入学习。