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在线词典
工具整理
工具一介绍:
- 首先复制网页的cURL请求,注意一定要是复制为(bash)
- 将复制的结果粘贴到工具以的网页中,即可得到完整的请求代码,复制到编译器运行即可。
工具二介绍:
- 由于在GO中,一般是通过构建结构体来获取解析JSON数据的,所以要先建立对应数据的结构体,利用工具二可以非常便捷地做到这一步。
-
首先复制相应内容。
-
将相应粘贴到工具二中,然后点击“转换-嵌套”即可获取解析数据的结构体。
- 接下来是项目实战
- 我借用的是百度翻译的接口,和视频中有所不同,百度翻译的请求是一个字符串"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"`
}
- 第二步定义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)
}
- 最后主函数输出Data
func main() {
word := "good"
res := query(word)
for i, v := range res.Data {
fmt.Println(i, v)
}
}
3.3socket5代理
Go 语言实现的 SOCKS5 代理服务器
功能概述
- 支持 SOCKS5 协议的认证过程。
- 处理客户端的连接请求,并进行认证。
- 解析客户端发送的目标地址和端口信息。
- 建立与目标服务器的连接。
- 在客户端和目标服务器之间转发数据。
代码结构
main.go:程序入口,监听指定端口,接受客户端连接并处理。auth.go:处理 SOCKS5 认证过程。connect.go:解析客户端请求,建立与目标服务器的连接,并进行数据转发。
关键代码解析
- 认证过程
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
}
- 连接处理
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
}