GO语言工程实践课后作业 | 青训营

65 阅读4分钟

    ~~~~在学习了Go语言的基础语法后,我们就要通过实践练习来巩固知识点,在接下来的课程中。老师使用了三个实例来给我们巩固基础:猜谜游戏、在线词典、SOCKS5代理。在课后·我也是亲自动手实践了这些项目,以下是我实践后得出的感悟与思考。
    ~~~~第一个项目是最简单的猜谜游戏,它的要求是:首先通过随机数种子生成随机数,然后用户输入自己所猜测的数字,程序会告知用户猜测的数字与真正数字的大小关系,直到最后猜中,才会返回。
    ~~~~以下是代码:


import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

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

	secretNumber := rand.Intn(maxNum)

	fmt.Printf("随机生成的数字: %d\n", secretNumber)
	fmt.Println("Please input your guess:")
	reader := bufio.NewReader(os.Stdin)
	for {
		input, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println("An error occured while reading input. Please try again", err)
			continue
		}
		input = strings.Trim(input, "\r\n")

		guess, err := strconv.Atoi(input)
		if err != nil {
			fmt.Println("Invalid input. Please enter an integer value")
			continue
		}
		fmt.Println("You guess is", guess)
		if guess > secretNumber {
			fmt.Println("Your guess is bigger than the secret number. Please try again")
		} else if guess < secretNumber {
			fmt.Println("Your guess is smaller than the secret number. Please try again")
		} else {
			fmt.Println("Correct, you Legend!")
			break
		}
	}

}

    ~~~~代码的实现很好理解:首先,使用*reader := bufio.NewReader(os.Stdin)读取输入的结果;然后,用input, err := reader.ReadString('\n')*读取输入的结果;最后,添加循环判断语句,直到结果成功输出。
    ~~~~第二个案例是在线词典,这一部分代码主要由抓包、代码生成和解析response body组成。它使用Go语言发送http请求,实现翻译和词典查询的功能。
    ~~~~以下是代码部分:

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
)

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"hello"}`)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,ar;q=0.8")
	req.Header.Set("app-name", "xy")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "990faa53baf70befe77f49b01b2daad7")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	req.Header.Set("x-forwarded-for", "1.1.1.1")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

    ~~~~在这一项目中,我们首先需要将预选的网站发出请求,然后在本地接收,实现在线翻译。
    ~~~~第三个项目是SOCKS5代理的实现。要完成这一项目,首先就要先仔细了解SOCKS5协议的工作原理和运作流程:Socks 协议是一种代理 (Proxy) 协议,它在使用TCP/IP协议的前端机器和服务器机器之间扮演一个中介角色,SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器,模拟了一个前端的行为。这一协议诞生于互联网早期,可以让授权的用户通过单个端口去访问内部的所有资源,同时满足了安全与便捷两大要素。
    ~~~~以下是代码部分:


import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const (
	socks5Ver   = 0x05
	cmdBind     = 0x01
	atypeIPV4   = 0x01
	atypeHOST   = 0x03
	atypeIPV6   = 0x04
)

func main() {
	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)
	}
}

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed: %v", conn.RemoteAddr(), err)
		return
	}
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v connect failed: %v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) error {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed: %w", err)
	}
	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) 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)
	}
	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)

	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
}

    ~~~~在以上代码中,我们使用SOCXS5协议实现了一个简单的代理服务,它在监听本地的 1080 端口,连接客户端,然后进行SOCXS5认证,实现目标服务器和客户端的数据联通。
    ~~~~以上便是我在三个课后作业中的实践过程,其过程的艰辛与困难绝非一言可概括的。不过,在实践过程中,我也收获了许多知识,这些都是在课堂上没有完全掌握的,可以说是很有价值的。