Go语言实战小项目 | 青训营笔记

74 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

一、猜谜游戏

1.1生成数字

首先,猜谜游戏需要有一个需要猜谜的数字。该数字应该用rand函数随机生成

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

注意需要使用time.Now().UnixNano()来作为随机数种子。获取当前时间的 Unix 时间戳(以纳秒为单位)。因为时间戳是不断变化的,所以使用当前时间戳作为随机种子能够保证生成的随机数序列是不同的 如果不采用此随机种子,生成的随机数就会一直是同一个。

1.2读取用户输入的数字

此功能类似于c语言里的scanf函数,作用是读取用户输入的数字。 调用的模块包:

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

其中bufio包提供了带缓存的 I/O 接口,其中包括 bufio.NewReader 函数,可以用来创建一个带缓存的读取器,由此可以实现读取用户输入的功能

reader := bufio.NewReader(os.Stdin)

1.3实现输入数字与猜谜数字的大小判断逻辑

strings 包中的 Trim 函数可以用于将字符串首尾的指定字符去掉,在这里传入的第二个参数是 "\r\n",表示将字符串首尾的回车和换行符去掉。

input = strings.Trim(input, "\r\n")//去掉换行符和收尾的\r \n

guess, err := strconv.Atoi(input)
if err != nil {//判断是否有错误信息,如果err不为 nil 则证明函数调用失败,程序错误
   fmt.Println("Invalid input. Please enter an integer value")
   continue//此处的continue是考虑到外面还有一个for循环,
   //这次的输入形式可能有问题从而导致等于nil,不应该用breakreturn。
}
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
}

1.4实现循环逻辑

没啥好说的,外面套个for循环就行了,记得写成死循环。

二、在线词典

2.1 抓包功能的实现

我使用的是edge浏览器。首先打开彩云小译网页页面,按f12。出现:

image.png

找到请求方法为post的页面,然后复制下curl(bash)

  -H 'authority: api.interpreter.caiyunai.com' \
  -H 'accept: application/json, text/plain, */*' \
  -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
  -H 'app-name: xy' \
  -H 'content-type: application/json;charset=UTF-8' \
  -H 'device-id;' \
  -H 'origin: https://fanyi.caiyunapp.com' \
  -H 'os-type: web' \
  -H 'os-version;' \
  -H 'referer: https://fanyi.caiyunapp.com/' \
  -H 'sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76' \
  -H 'x-authorization: token:qgemv4jr1y38jyq6vhvi' \
  --data-raw '{"trans_type":"en2zh","source":"good","user_id":"63c36f541120a60014f8bda4"}' \
  --compressed

2.2代码生成

进入代码生成网址 Convert curl commands to Go (curlconverter.com)

生成代码:

package main 
import ( 
"fmt" 
"io/ioutil" 
"log" 
"net/http" 
"strings" 
) 
func main() { 
client := &http.Client{} 
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good","user_id":"63c36f541120a60014f8bda4"}`) 
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,en;q=0.8,en-GB;q=0.7,en-US;q=0.6") 
req.Header.Set("app-name", "xy") 
req.Header.Set("content-type", "application/json;charset=UTF-8") 
req.Header.Set("device-id", "") 
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", `"Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"`) 
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/108.0.0.0 Safari/537.36 Edg/108.0.1462.76") 
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi") 
resp, err := client.Do(req) 
if err != nil {
log.Fatal(err) 
} 
defer resp.Body.Close() 
bodyText, err := ioutil.ReadAll(resp.Body) 
if err != nil { 
log.Fatal(err) 
} 
fmt.Printf("%s\n", bodyText)
}

2.3 解析response body

JSON转Golang Struct - 在线工具 - OKTools

然后对生成的结果json序列化:

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
)

type DictRequest struct {
   TransType string `json:"trans_type"`
   Source    string `json:"source"`
   UserID    string `json:"user_id"`
}

func main() {
   client := &http.Client{}
   request := DictRequest{TransType: "en2zh", Source: "kid"}
   buf, err := json.Marshal(request)
   if err != nil {
      log.Fatal(err)
   }
   var data = bytes.NewReader(buf)
   req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
   if err != nil {
      log.Fatal(err)
   }
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("DNT", "1")
   req.Header.Set("os-version", "")
   req.Header.Set("sec-ch-ua-mobile", "?0")
   req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
   req.Header.Set("app-name", "xy")
   req.Header.Set("Content-Type", "application/json;charset=UTF-8")
   req.Header.Set("Accept", "application/json, text/plain, */*")
   req.Header.Set("device-id", "")
   req.Header.Set("os-type", "web")
   req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
   req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
   req.Header.Set("Sec-Fetch-Site", "cross-site")
   req.Header.Set("Sec-Fetch-Mode", "cors")
   req.Header.Set("Sec-Fetch-Dest", "empty")
   req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
   req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
   req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
   resp, err := client.Do(req)
   if err != nil {
      log.Fatal(err)
   }
   defer resp.Body.Close()
   bodyText, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Printf("%s\n", bodyText)
}

之后将json结构体导入到go结构体里面,剩下的把代码生成的那串代码取来即可。

三、sockes5代理

原理介绍

sockes5是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。根据OSI七层模型来划分,sockes5属于会话层协议,位于表示层与传输层之间。 当防火墙后的客户端要访问外部的服务器时,就跟sockes5代理服务器连接。该协议设计之初是为了让有权限的用户可以穿过过防火墙的限制,使得高权限用户可以访问外部资源。经过10余年的时间,大量的网络应用程序都支持sockes5代理。

socks5协议解析之授权认证

如果要与socks5服务器建立TCP连接,客户端需要先发起请求来对协议的版本及其认证方式。这里就是客户端请求服务器的请求格式:

image.png

当服务器端收到了客户端的请求之后,就会在响应客户端的请求,服务端需要客户端提供哪种验证方式的信息。

一开始就是先判断三个数据VER ,NMETHODS ,METHODS 。接着如下就没就是获取到客户端发送过来的模式,默认就是不需要密码的。

for _, v := range s.METHODS 
{ 
if v == METHOD_CODE 
{ useMethod = METHOD_CODE 
} 
} 
if s.VER != SOCKS_VERSION 
{ return nil, errors.New("该协议不是socks5协议") 
} 
if useMethod != METHOD_CODE 
{ return nil, errors.New("协议错误, 加密方法不对") 
} 
resp := []byte{SOCKS_VERSION, useMethod}

对于sockes5部分我也是一知半解,所以找了一篇相关的文章供大家阅读。 socks5协议原理学习 - 腾讯云开发者社区-腾讯云 (tencent.com)