Day03:豆包MarsCode 技术训练营第三课Go语言课程实践 | 豆包MarsCode AI 刷题

93 阅读5分钟

3.1猜谜2游戏

  • 猜谜游戏也可以叫踩地雷游戏,玩家猜测一个随机的数,如果猜对了,则游戏结束,没猜对的话,提示玩家猜测过大还是过小。
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	maxNum := 100
	rand.Seed(time.Now().UnixNano())
	num := rand.Intn(maxNum)

	var guess int
	fmt.Println("请输入你的猜测")
	for {
		fmt.Scanf("%v", &guess)
		fmt.Printf("你的猜测是%v\n", guess)
		if num == guess {
			fmt.Println("恭喜你猜对啦")
			return
		} else if guess > num {
			fmt.Println("你猜的有点大")
		} else if guess < num {
			fmt.Println("你猜的有点小")
		}
	}

}

  • 这个代码比课程里的要简便一些,用fmt.Scanf()方法得到输入值,更直观,也对c语言玩家更友好和熟悉,整体逻辑不难,主要是为了敲一敲熟悉语法。

3.2在线词典

工具整理

工具一介绍:
  1. 首先复制网页的cURL请求,注意一定要是复制为(bash)

屏幕截图2024.11.06_15.44.42.png

  1. 将复制的结果粘贴到工具以的网页中,即可得到完整的请求代码,复制到编译器运行即可。

1730879453255.png

工具二介绍:
  • 由于在GO中,一般是通过构建结构体来获取解析JSON数据的,所以要先建立对应数据的结构体,利用工具二可以非常便捷地做到这一步。
  1. 首先复制相应内容。 image.png

  2. 将相应粘贴到工具二中,然后点击“转换-嵌套”即可获取解析数据的结构体。

image.png

  • 接下来是项目实战
  1. 我借用的是百度翻译的接口,和视频中有所不同,百度翻译的请求是一个字符串"kw=*****",先定义好请求和响应的结构体如下。
type DirectRequest struct {
	Kw string `json:"kw"`
}
type DirectResponse struct {
	Errno int `json:"errno"`
	Data  []struct {
		K string `json:"k"`
		V string `json:"v"`
	} `json:"data"`
	Logid int `json:"logid"`
}
  1. 第二步定义query函数,具体的步骤都已经写在了注释中。
func query(word string) DirectResponse {
	// 1.设置数据请求体
	client := &http.Client{}
	request := DirectRequest{Kw: word}
	data := strings.NewReader("kw=" + request.Kw)
	req, err := http.NewRequest("POST", "https://fanyi.baidu.com/sug", data)
	if err != nil {
		log.Fatal(err)
	}
	// 2.设置请求头
	req.Header.Set("Accept", "*/*")
	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("Connection", "keep-alive")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Cookie", `BDUSS=JsTW1WYURkTGxEbVljZ2w1fkx4RFdzbC15cHhJdFI3Q3RmUE83V05MR2RmUEZtSVFBQUFBJCQAAAAAAQAAAAEAAAAYDbsnvOi~4MbTy9jX38zsz8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ3vyWad78lmd3; BDUSS_BFESS=JsTW1WYURkTGxEbVljZ2w1fkx4RFdzbC15cHhJdFI3Q3RmUE83V05MR2RmUEZtSVFBQUFBJCQAAAAAAQAAAAEAAAAYDbsnvOi~4MbTy9jX38zsz8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ3vyWad78lmd3; BIDUPSID=2486F7698EEC3C0D0321BE46AE03D66A; PSTM=1727959759; BAIDUID=DBE2B5E9AAE191B758AE88CB70F5835E:FG=1; ZFY=ehfQ0CjQc:B3OzA5I8HWDqyKTCAjnMYYTn:AAlwS8QVGc:C; BAIDUID_BFESS=DBE2B5E9AAE191B758AE88CB70F5835E:FG=1; H_PS_PSSID=60272_60854_61007; ariaDefaultTheme=undefined; ab_sr=1.0.1_OTE3MjE3NzRkMmIzOThkNTllMjUzMGQ3NDUxMWU1NmNjMTc2MWZmZDg2MThkMDMwMGM0YTBjMmYzYzg0ODVhNGI0NGY0ODQ2MThmYzM4Y2RmYTkxNTIyNDU0YzUwOGNkY2U1ZTA4MDA1OWM4ZWZhNmY1ZmNhODczZDUxMzNlNzZlY2E2YzJmNmM3ZWFkYWI0NGZjNGY2MGIwNWQ3ODRiMQ==; RT="z=1&dm=baidu.com&si=0c92afb5-e665-4aaf-9620-b2c982f95b6e&ss=m34j2zl8&sl=1&tt=3g9&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=48q"`)
	req.Header.Set("Origin", "https://fanyi.baidu.com")
	req.Header.Set("Referer", "https://fanyi.baidu.com/mtpe-individual/multimodal")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Site", "same-origin")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	// 3.发送请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	// 4.关闭请求
	defer resp.Body.Close()
	// 5.获得响应体的数据
	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))
	}
	// 6.将响应体的数据用响应结构体进行接收
	var dictResponse DirectResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	return dictResponse
	// fmt.Printf("%#v\n", dictResponse)
}
  1. 最后主函数输出Data
func main() {
	word := "good"
	res := query(word)
	for i, v := range res.Data {
		fmt.Println(i, v)
	}
}

3.3socket5代理

Go 语言实现的 SOCKS5 代理服务器

功能概述
  1. 支持 SOCKS5 协议的认证过程。
  2. 处理客户端的连接请求,并进行认证。
  3. 解析客户端发送的目标地址和端口信息。
  4. 建立与目标服务器的连接。
  5. 在客户端和目标服务器之间转发数据。
代码结构
  • main.go:程序入口,监听指定端口,接受客户端连接并处理。
  • auth.go:处理 SOCKS5 认证过程。
  • connect.go:解析客户端请求,建立与目标服务器的连接,并进行数据转发。
关键代码解析
  1. 认证过程
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
    // 读取客户端发送的版本号
    ver, err := reader.ReadByte()
    if err!= nil {
        return fmt.Errorf("read ver failed:%w", err)
    }
    // 检查版本号是否为 SOCKS5
    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)
    }
    // 发送认证响应,表示不需要认证
    _, err = conn.Write([]byte{socks5Ver, 0x00})
    if err!= nil {
        return fmt.Errorf("write failed:%w", err)
    }
    return nil
}
  1. 连接处理
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
    // 读取客户端请求头
    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)
    }
    // 检查命令是否为 CONNECT
    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)
    // 发送响应,表示连接成功
    _, 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
}