1 - Go 语言入门 + 简单实战 | 青训营笔记

111 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

我是先写完了后面的课堂知识部分,再按照课堂笔记模板硬套,所以层级没那么清晰,为了阅读方便就先把内容较短的、个人理解相关的放前面了

个人总结部分

本节重点

  • Go 语言是什么及基础语法讲解
  • 三个蛮有意思的实战,分别涉及
    • 基础交互、读写、随机数
    • 抓包、json序列化与反序列化
    • 网络编程

课后小结

课程还是挺有意思的,知识点很密集,侥幸于自己至少预习了语法。笔记越做越觉得是不是有点没必要……之后会尝试更简洁地记录自己重点需要关注的部分。另外读代码既需要记录,又不好直接发布,再看看吧

以下是知识记录


什么是 Go 语言

  1. 高性能、高并发
    1. 不需要寻找高性能第三方库,使用标准库或基于标准库的第三方库即可完成高性能、高并发开发
  2. 语法简单、学习曲线平缓
    1. 基于 C 语言并大量简化,上手容易
  3. 丰富的标准库
  4. 完善的工具链
    1. 编译、注释、测试、包管理等等都有内置工具,支持单元测试等
  5. 静态链接
    1. 不需要像 java 那样附加 JRE
  6. 快速编译
  7. 跨平台
    1. 无需配置交叉编译环境
  8. 垃圾回收

配置环境略去

基础语法

hello, world

package main // main包 -> 程序入口包,这个文件就是程序的入口文件

import (
	"fmt" // 格式化输入输出
)

func main() {
	fmt.Println("hello world")
}

switch 分支

switch var1 {
    case val1:
        ...
    case val2:
        ...
    case val3, val4 :
        ...
    default:
        ...
}
  • 相比C或Java,Go的switch更强大,var1可以是包括字符串在内的任意数据类型, val1val2 则可以是同类型的任意值
  • Java中若没有break,会一直执行到最后,Go中不会
    • switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough
  • 可以同时测试多个可能符合条件的值,使用逗号分割它们,如val3, val4

切片

Go 语言切片是对数组的抽象

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片 ("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

感觉类似 Arraylist

声明

// 声明一个未指定大小的数组来定义切片
var identifier []type 

// 使用 make() 函数来创建切片
var slice1 []type = make([]type, len) 
// 也可以简写为
slice1 := make([]type, len)

Append 小tips

s = append(s,"d") -> 执行 append 可能扩容并产生新切片,需要重新赋值回切片 s

切片其他

菜鸟教程

指针

Go 中的指针相比起来没有那么强大,主要用在修改拷贝参数

image.png

在 main 中直接调用 add2 将不起作用

其他

还讲了 变量声明, if else, for, 数组,map, 结构体,错误处理, 字符串操作, 字符串和数值类型转换, 格式化输入输出, 时间, 进程 等,都是比较基础的东西,略去

实战 remark

生成随机数

image.png

如果不使用 rand.Seed() 来初始化随机数种子,每次运行结果都是一样的;这里使用时间戳初始化

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	maxNum := 100
	rand.Seed(time.Now().UnixNano())
	secretNumber := rand.Intn(maxNum)
	fmt.Println("The secret number is ", secretNumber)
}

结果会在 0-100 中间

在线词典

抓包

首先抓包,在 彩云小译 进行测试。点击翻译后,在 network 选项卡的 dict (请求方法为 request) 能解析出我们需要的数据

image.png

请求这么一大堆信息会很麻烦,我们用一些奇巧淫技

使用 cURL

右键我们需要的请求,复制为 cURL

image.png

会返回一大堆命令。到这个网站 复制粘贴这些命令并选择 go 语言,自动返回一个函数

image.png

复制到 IDE 中,发现有几行有错误,删掉即可

代码部分如下

image.png

可以输出一大串 Json

Json 序列化

序列化是将对象状态转换为可保持或传输的格式的过程

现在发送的请求是固定的,我们需要设置变量并 序列化 来改变请求

在 Go 中,构造与json字段名称相同的结构体并调用json.Marshal即可

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}
request := DictRequest{TransType: "en2zh", Source: "good"}
buf, err := json.Marshal(request)

注意Marshal返回的是个byte数组而非真正的字符串,需要 var data = bytes.NewReader(buf) 而非之前的 var data = strings.NewReader({"trans_type":"en2zh","source":"good"})

image-20230115213558647

解析 response

Go 可以像 js 或 python 那样用 []. 取值,但不是最佳实践。更常用的方式应该是和 request 一样去写一个结构体,然后再把返回的 json 字段反序列化到结构体内

但是,我们可以发现……这 json 太复杂了要一个个定义 structure 不如让程序员去死。所以我们同样用奇巧淫技(代码生成)

JSON 转 Golan Struct

把浏览器复制的字段再复制过来,点击 转换-嵌套 (转换-展开 得到多个独立结构体,这里没有必要),复制到我们程序中即可

image.png

我们试着不再 print 全部,而是只打印需要的、解析好的:

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
// 解释部分是个数组,我们循环打印
    for _, item := range dictResponse.Dictionary.Explanations { 

        fmt.Println(item)

    }

添加检测问题的模块,若状态码不是两百,报错

if resp.StatusCode != 200 {

        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))

    }

最后,改变代码,从 Args 拿到 word,把 word 设为查询的参数即可,具体步骤略去

Socks5 代理

介绍

完成 TCP echo

windows 日常要装一堆东西才能跑,心态小崩,晚点试试 WSL

参考这个看代码,讲解的蛮清楚了。注意在 process 中要关闭连接再继续, 执行完回到无限循环后重新开始新的连接

完成 SOCKS

看代码吧

  1. 认证:浏览器会给代理服务器发送报文,会有三个字段,我们判断这三个字段是否合法,并返回一个包,告诉浏览区我们选择哪种方式
  2. 请求:接受浏览器发回的报文并一个个读出,得到要去建立连接的地址和端口号并回包
  3. relay:建立连接、建立双向数据转发(浏览器拷贝到服务器,服务器拷贝到浏览器);注意默认异步直接跳到结束,需要 context.WithCancel 等待