青训营X豆包MarsCode 技术训练营后端笔记——Go语言实战案例 | 豆包MarsCode AI刷题

113 阅读6分钟

终于来到了go语言的实战环节,前面的课程听的有些迷迷糊糊,现在来上手试一下go语言到底是怎么个事~有三个实战案例,猜数字、在线字典

猜数字

这个案例比较简单,非常适合入门,下面罗列一下知识点和遇到的问题

  • 随机数的生成:在这里使用到了"math/rand"库里的rand.Intn(n int)来生成随机数,该函数生成一个半开半闭区间[0,n)中的随机数。(因为python写习惯了,总是忘记变量声明的var或者:=)
            var maxNum = 100    // 随机数的最大值
            var secretNum = rand.Intn(maxNum)  
            fmt.Println(secretNum)
    
    注意:这里虽然课程说要设置随机数种子才能每次生成不同的随机数,实际上现在的go语言版本已经支持内置的随机数种子,也就是不在需要显式的设置随机数种子了,可以通过rand.Seed(time.Now().UnixNano())来设置随机数种子,随机数种子为时间戳 ![[Pasted image 20241103185218.png]]
  • 用户输入输出:为了方便读取,这里使用bufio.NewReader 函数创建了一个带缓冲区的读取器,而输入源是操作系统提供的标准输入流os.Stdin,使用了reader中的ReadString操作来读取数据,参数'\n'表示遇到换行符则停止读取
    reader := bufio.NewReader(os.Stdin)  // 创建读取器
    input, err := reader.ReadString('\n')  // 从输入流中读取字符串数据
    
    注意:这里会把换行符也读取到input中 "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{ ... }
  • 结果展示:

Pasted image 20241104153205.png

在线字典

这个案例要求实现一个在线词典,通过调用api来实现词典查询,当输入一个词,返回该词对应的释义 主要知识点有1. 使用go发送http请求,2. 解析json,3. 代码生成提高开发效率

  1. 抓包
    • 词典api网址:彩云小译 在网站中输入要查询的词”melon“,翻译返回了”甜瓜“。用浏览器”检查“功能”网络“中查看POST发送请求,在dict处右键复制curl(bash) ![[Pasted image 20241114162909.png]]
    • 请求代码生成网址:curlconverter,将复制的curl 指令粘贴到该网址得到对应的go语言代码
    • 主要过程:过程分为三步——构造请求,发送请求,读取响应
      • 构造请求:
        1. 使用strings将请求内容的json字符串转为流的形式data
        2. 使用NewRequest创建请求req,参数包括请求类型请求api请求体
        3. 错误判断
        4. 使用req.Header.Set设置请求头
      • 发送请求:
        1. 创建发起HTTP请求的客户端client
        2. 使用client和请求req发送请求,得到响应resp
        3. 错误判断
      • 读取响应:
        1. 为了避免资源泄露,加一个defer关闭读取到的响应的body流
        2. readAll读取body,返回的是byte数组
  2. Json序列化
    • 为了使得输入不固定,需要创建一个结构体并对其序列化,得到请求体的json字串。结构体字段包括:Trans_type和Source,这里还引入了一个结构体标签的概念,用于指定当把该结构体序列化为json字符串时,该字段对应的json键。例如:Trans_Type string `json:"trans_type"`
    • 使用marshal序列化返回的不再是字符串而是一个byte数组,因此在转为流的形式的时候应该使用bytes.NewReader
  3. Json反序列化
    • 这一步是为了解析http响应内容,其他语言的常规做法是解析后读取为dict或者map,golang更常见的做法是将响应解析为结构体,因此要创建一个响应内容的结构体。
    • 但是人工构造返回字段的结构体同样比较复杂,所以使用json转gostruct的代码生成平台将json转golang struct。
    • 定义好结构体后,创建一个空的结构体实例,并使用json.Unmarshal把body结果反序列化到结构体中。使用%#v来打印结构体的细节内容。
  4. 完善字典查询功能
    • 首先是需要带参运行程序,这个参数就是要查询的单词,例如查询单词melon则使用go run dict.go melon,在main中使用os.Args来获取需要的参数,这里注意Args[0]获取的是代码路径,而从[1]开始才是传入的参数
    • 把查询字典的过程封装为函数queryDict(word),所有请求和打印的过程都在这里面实现
    • 最终可以只打印需要的内容,例如音标,解释,从结构体中获取并输出即可。
  5. 实现效果: 运行go run dict.go hello

Pasted image 20241114210036.png

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练中学的实现: image.png image.png image.png

因为对网络相关的知识不太熟悉,之后有空再继续更新sockS5代理服务器的实现。