这是我参与【第五届青训营】伴学笔记创作活动的第1天|(二)
一、本堂课重点内容
Go实战案例,练习
二、实战案例
1、猜谜语游戏
1-生成随机数
**注意:先设置随机数种子,否则生成的随机数序列相同 **
rand.Seed(time.Now().UnixNano())
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is", secretNumber)
}
看看运行效果对比:
【未设置随机数种子】
生成的随机数相同
【设置随机数种子】
设置之后,生成的随机数都在0-100之间,且不一样
2-读取用户输入
添加以下代码
// 读取用户输入
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil{
fmt.Println("An error occured while reading input.Please try again", err)
return
}
// 去掉\n
input = strings.TrimSuffix(input, "\n")
// 转为数字
guess, err := strconv.Atoi(input)
if err != nil{
fmt.Println("Invalid input.Please enter an integer value")
return
}
fmt.Println("You guess is", guess)
运行效果
3-实现判断逻辑
// 判断逻辑
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!")
}
运行效果
4-添加循环
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
//fmt.Println("The secret number is", 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)
return
continue
}
// 去掉\n
input = strings.TrimSuffix(input, "\n")
// 转为数字
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input.Please enter an integer value")
return
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
}
}
}
运行效果
2、在线词典
介绍:用户可以在命令行里面查询一个单词,我们能通过第三方的API查询到单词的翻译并打印出来。
1-抓包
网址 : fanyi.caiyunapp.com/
输入“你好”,点击翻译,浏览器会发送一系列请求,翻译单词的请求是“dict”,是http的post请求。 json里面有两个字段,source是要查询的单词。
2-golang代码生成
在golang中发送post请求,代码构造比较麻烦,可以在浏览器中通过右键浏览器里面的copy as curl,copy完成之后在终端粘贴一个curl命令,可以成功返回一大串json。
复制之后粘贴到网站:curlconverter.com/go/
复制go代码到编辑器中,有几个header比较复杂,生成代码有转义导致的编译错误,删掉即可
运行:
3-生成request body
生成一段JSON,常用的方式是先构造一个结构体:
这里定义一个变量,初始化每个结构体成员,再调用json.Marshal来得到这个序列化之后的字符串,不过这里是字节数组,所以后面使用bytes.NewReader来构造request上的body,其他的代码不变。
运行:
4-解析response body
将上一步运行出来的代码复制到oktools.net/json2go 网址:
将response结构体复制到编译器中并修改以下代码
运行:
5-打印结果
3、SOCKS5代理服务器
SOCKS5协议是代理协议,但不能用来翻墙,它的协议都是明文传输
1-TCP echo server
工作逻辑:客户端发送什么就回复什么
讲解:首先在 main 函数里面先用 net.listen 去监听一个端口,会返回一个 server,然后在一个死循环里面,每次去 accept 一个请求,成功就会返回一个连接。接下来的话我们在一个 process 函数里面去处理这个连接。接下来是这个 process 函数的实现,首先第一步的话会先加一个 deferconnection.close0),defer 是 Golang 里面的一个语法,这一行的含义就是代表在这个函数退出的时候要把这个连接关掉,否则会有资源的泄露。用 bufio.NewReader 来创建一个 带缓冲的只读流,这个在前面的猜迷游戏里面也有用到,带缓冲的流的作用是,可以减少底层系统调用的次数,比如这里为了方便是一个字节个字节的读取,但是底层可能合并成几次大的读取操作。并且带缓冲的流会有更多的一些工具函数用来读取数据。我们可以简单地调用那个 readbyte 函数来读取单个字节,再把这一个字节写进去连接。
package main
import (
"bufio"
"log"
"net"
)
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)
for {
b, err := reader.ReadByte()
if err != nil {
break
}
_, err = conn.Write([]byte{b})
if err != nil {
break
}
}
}
当发生错误端口被占用时:
2-auth
认证阶段
3-请求阶段
读取携带url或者ip地址+端口的包,然后把它打印出来
4-relay阶段
直接用net.Dial建立一个TCP连接,defer关闭连接
三、课后个人总结
跟着写了几个实战例子,多练练还可以。