这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
Day2
Go 语言的实战案例
本节课将通过详解猜数字、在线词典、Socks5代理三个实际案例,帮助大家从入门到实操,轻松上手 Go 语言。
猜数字
程序随机生成一个0~100的数字,用户根据程序的反馈(”大了“、“小了”)调整自己的猜想,直到猜对那个数字。
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
// 用时间戳来初始化随机数种子
// 计算机的随机数字是伪随机且可复现的,所以每次运行都需要设置一个不同的随机种子。
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNumber)
fmt.Println("input your guess")
// 从标准输入读取用户输入
reader := bufio.NewReader(os.Stdin)
for {
// 读取直到第一次遇到'\n'字节,返回一个包含已读取的数据和'\n'字节的字符串,如"50\n"
input, err := reader.ReadString('\n')
// 基操勿烦
if err != nil {
fmt.Println("error. try again", err)
continue
}
// 将前后端所有"\r\n"包含的utf-8码值都去掉,也就是去掉'\r'和'\n'
input = strings.Trim(input, "\r\n")
// 将字符串转换成数字,反过来有Itoa()
// Atoi是ParseInt(s, 10, 0)的简写,10进制,0代表int。
// Itoa是FormatInt(i, 10) 的简写,返回i的10进制的字符串表示。
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("enter an integer value")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("Your guess is bigger")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller.")
} else {
fmt.Println("Correct!")
break
}
}
}
在线词典
成品展示
go run main.go hello
hello UK: [ˈheˈləu] US: [həˈlo]
int.喂;哈罗
n.引人注意的呼声
v.向人呼(喂)
自动生成demo
F12查看浏览器Network下dict的Headers、Payload、Preview。
右键dict Copy as cURL --> Convert curl commands
// 1. 创建请求
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", str_json_data)
// 2. 设置请求头
req.Header.Set("Connection", "keep-alive")
// 3. 发起请求
resp, err := client.Do(req)
defer resp.Body.Close()
// 4. 读取响应
bodyText, err := ioutil.ReadAll(resp.Body)
err = json.Unmarshal(bodyText, &dictResponse)
构造request.body
// post_json_data 改用结构体
// strings.NewReader(json_str)
bytes.NewReader(json.Marshal(struct))
解析response.body
json.Unmarshal(ioutil.ReadAll(resp.Body), &structResponse)
// fmt.Fprintf(os.Stderr, `usage: xxx`)
Socks5代理
三个阶段:握手、认证、请求、relay(接力、转发)。
v1:握手
process():echo请求。
# 不能用curl
nc 127.0.0.1 1080
echo
echo
v2:认证
auth():约定认证方式。
v3:请求
connect():接收local请求并回复local已接收成功。
v4:转发
connect():+请求remote,响应local。
curl --socks5 127.0.0.1:1080 -v https://www.qq.com
// main():调用process()
// conn: proxy上来自local的连接
conn, err := server.Accept()
// process():调用auth()、connect()
// reader: proxy读取来自local的数据
reader := bufio.NewReader(conn)
// connect():请求、转发
// dest: proxy发出到remote(addr:port)的连接
dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
// 接通目的ip,回包,通知local请求转发成功。
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
// 转发阶段
// context包提供上下文机制在 goroutine 之间传递 deadline、取消信号(cancellation signals)或者其他请求相关的信息。
// context.WithCancel 函数能够从 context.Context 中衍生出一个新的子上下文并返回用于取消该上下文的函数。一旦我们执行返回的取消函数,当前上下文以及它的子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号。
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() // 幂等,可多次调用,但实际上没有意义
// io.Copy(dst Writer, src Reader) (written, err)
// 从src读取数据,并写入到dst
go func(){
// 读取reader的数据并写入dest,即转发请求
_, _ = io.Copy(dest, reader)
cancelFunc() // 出错的时候调用(不太理解这说法)
}()
go func(){
// 读取dest的数据并写入conn,即转发响应
_, _ = io.Copy(conn, dest)
cancelFunc() // 出错的时候调用(不太理解这说法)
}()
// 等待ctx执行完成,也就是等待cancelFunc执行
<-ctx.Done()