终于来到了go语言的实战环节,前面的课程听的有些迷迷糊糊,现在来上手试一下go语言到底是怎么个事~有三个实战案例,猜数字、在线字典
猜数字
这个案例比较简单,非常适合入门,下面罗列一下知识点和遇到的问题
- 随机数的生成:在这里使用到了"math/rand"库里的
rand.Intn(n int)来生成随机数,该函数生成一个半开半闭区间[0,n)中的随机数。(因为python写习惯了,总是忘记变量声明的var或者:=)注意:这里虽然课程说要设置随机数种子才能每次生成不同的随机数,实际上现在的go语言版本已经支持内置的随机数种子,也就是不在需要显式的设置随机数种子了,可以通过var maxNum = 100 // 随机数的最大值 var secretNum = rand.Intn(maxNum) fmt.Println(secretNum)rand.Seed(time.Now().UnixNano())来设置随机数种子,随机数种子为时间戳 ![[Pasted image 20241103185218.png]] - 用户输入输出:为了方便读取,这里使用
bufio.NewReader函数创建了一个带缓冲区的读取器,而输入源是操作系统提供的标准输入流os.Stdin,使用了reader中的ReadString操作来读取数据,参数'\n'表示遇到换行符则停止读取注意:这里会把换行符也读取到input中reader := bufio.NewReader(os.Stdin) // 创建读取器 input, err := reader.ReadString('\n') // 从输入流中读取字符串数据"strings"库中的Trim函数可以帮助删除换行符,对输入字符串input进行清洗:利用input, err = strings.Trim(input,"\r\n")"strconv"的Atoi方便地把输入字符串转换为整型guess, err = strconv.Atoi(input) if err!= nil {// 错误判断 fmt.Println("输入有误") } - 判断输入的大小:使用if else分支判断语句来判断输入的大小
if guess == secretNum { fmt.Println("恭喜你猜对了") return } else if guess > secretNum { fmt.Println("你猜大了") } else { fmt.Println("你猜小了") } - 循环判断直到用户输入正确的数字:直接套一个for循环不加停止条件~如果遇到问题就continue开启下一次循环 for{ ... }
- 结果展示:
在线字典
这个案例要求实现一个在线词典,通过调用api来实现词典查询,当输入一个词,返回该词对应的释义 主要知识点有1. 使用go发送http请求,2. 解析json,3. 代码生成提高开发效率
- 抓包
- 词典api网址:彩云小译 在网站中输入要查询的词”melon“,翻译返回了”甜瓜“。用浏览器”检查“功能”网络“中查看POST发送请求,在dict处右键复制curl(bash) ![[Pasted image 20241114162909.png]]
- 请求代码生成网址:curlconverter,将复制的curl 指令粘贴到该网址得到对应的go语言代码
- 主要过程:过程分为三步——构造请求,发送请求,读取响应
- 构造请求:
- 使用strings将请求内容的json字符串转为流的形式data
- 使用
NewRequest创建请求req,参数包括请求类型,请求api,请求体 - 错误判断
- 使用
req.Header.Set设置请求头
- 发送请求:
- 创建发起HTTP请求的客户端client
- 使用client和请求req发送请求,得到响应resp
- 错误判断
- 读取响应:
- 为了避免资源泄露,加一个defer关闭读取到的响应的body流
readAll读取body,返回的是byte数组
- 构造请求:
- Json序列化
- 为了使得输入不固定,需要创建一个结构体并对其序列化,得到请求体的json字串。结构体字段包括:Trans_type和Source,这里还引入了一个结构体标签的概念,用于指定当把该结构体序列化为json字符串时,该字段对应的json键。例如:Trans_Type string `json:"trans_type"`
- 使用marshal序列化返回的不再是字符串而是一个byte数组,因此在转为流的形式的时候应该使用
bytes.NewReader
- Json反序列化
- 这一步是为了解析http响应内容,其他语言的常规做法是解析后读取为dict或者map,golang更常见的做法是将响应解析为结构体,因此要创建一个响应内容的结构体。
- 但是人工构造返回字段的结构体同样比较复杂,所以使用json转gostruct的代码生成平台将json转golang struct。
- 定义好结构体后,创建一个空的结构体实例,并使用
json.Unmarshal把body结果反序列化到结构体中。使用%#v来打印结构体的细节内容。
- 完善字典查询功能
- 首先是需要带参运行程序,这个参数就是要查询的单词,例如查询单词melon则使用
go run dict.go melon,在main中使用os.Args来获取需要的参数,这里注意Args[0]获取的是代码路径,而从[1]开始才是传入的参数 - 把查询字典的过程封装为函数
queryDict(word),所有请求和打印的过程都在这里面实现 - 最终可以只打印需要的内容,例如音标,解释,从结构体中获取并输出即可。
- 首先是需要带参运行程序,这个参数就是要查询的单词,例如查询单词melon则使用
- 实现效果:
运行
go run dict.go hello
SOCKS5代理服务器
什么是socks5协议? SOCKS5是一种诞生于互联网早期的网络代理协议,主要用于在客户端和服务器之间传递网络数据。它是一种明文传输的协议。 SOCKS5协议的工作过程包括客户端与服务器的身份验证、代理服务器响应客户端请求、客户端向代理服务器发送请求地址、代理服务器将请求转发给目标服务器,以及数据的转发。
什么是代理服务器 代理服务器由代理程序和服务器组成,充当客户端和服务端的中介。提供转发功能,接受客户端请求,返回服务端响应报文。
编写一个简单的本地服务器
- 创建服务器
这段代码使用net包创建了一个tcp服务器server,并监听本地端口
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) //处理客户端连接 可以理解为启动子线程来处理链接 } - process做了什么? process函数持续不断地从客户端读取数据,然后又把读取到的数据返回给客户端。
- 尝试在本地实现,但是下载netcat时检测到病毒,因此就不再继续在本地实现了。
- 在AI练中学的实现:
因为对网络相关的知识不太熟悉,之后有空再继续更新sockS5代理服务器的实现。