这是我参与「第五届青训营 」笔记创作活动的第2天
实战案例一:猜谜游戏
在没有看老师给的代码之前,我们可以先根据自己的想法对该游戏进行思考。
- 首先我们要知道,这里面包括一个人机的互动,所以这里需要包含输入操作;
- 其次既然是猜谜,那么这个数一定是我们事先不知道的,可以由电脑来创造,其他语言中我们知道有一个函数
random,那么其实可以推测出go语言中很大可能也有这样一个随机数生成函数; - 接着我们要考虑到代码的复用性以及游戏的可持续性,既然是游戏,那么用户有可能有其他需求,而每次再猜一次都需要重新启动程序,未免有些麻烦,这里我们可以在用户体验上加上一个循环,并且使用户能够自己判断;
- 最后,每当用户输入一个数字,我们都应该给予一些帮助提醒,告知用户数值的范围(偏大或者偏小),并且需要保持每次设定的猜谜值是随机的。
我们可以自己去实现生成随机数的函数,也可以使用go语言提供的两种随机数生成方式
math/rand // 伪随机
crypto/rand // 真随机
math/rand
伪随机生成的数字是确定的,不论在什么机器、什么时间,只要执行的随机代码一样,那么生成的随机数就一样,例如:
func main() {
rand.Seed(2)
for i := 0; i < 4; i++ {
println(rand.Intn(100))
}
}
// output
86
86
92
40
golang使用一个seed作为source来生成伪随机数字,默认seed为1,只有seed固定了,那么所有随机数就固定了:
func(seed, 100) => xx,yy,zz
这里有一个坑:如果seed固定,那么每次程序重启后重新生成随机数会重复上一次的随机数
为了尽量随机性,那么我们可以每次使用不同的seed来启动程序,就可以保证每次启动都产生新的随机数,聪明的你肯定想到了使用时间戳
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 3; i++ {
println(rand.Intn(100))
}
}
使用ns时间戳可以保证每次重启seed都不一样,然后可以生成新的随机序列
crypto/rand
crypto/rand是为了提供更好的随机性满足密码对随机数的要求,在linux上已经有一个实现就是 /dev/urandom, crypto/rand 就是从这个地方读“真随机”数字返回,但性能比较慢
func main() {
for i := 0; i < 4; i++ {
n, _ := rand.Int(rand.Reader, big.NewInt(100))
println(n.Int64())
}
}
性能区别
name time/op
RandWithCrypto-8 272ns ± 3%
name time/op
RandWithMath-8 22.8ns ± 4%
// 差10倍
那么此时,我们就可以去结合思路和第一中生成随机数的方法去写代码了
实战案例二:命令行词典
这个案例在看到题目时,我不知道怎么做,听完老师讲的后整理了一下思路
-
首先这里使用到了一个网站彩云小译,在该页面打开开发者工具
-
按照下面的步骤可以看到获取的信息
-
复制链接,我这里以(cURL)bash的格式复制,得到一大串JSON
-
之后打开这个网址,将刚刚得到的数据复制到command中,将会生成对应的go代码
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://fanyi.caiyunapp.com/", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("authority", "fanyi.caiyunapp.com")
req.Header.Set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
req.Header.Set("cache-control", "max-age=0")
req.Header.Set("cookie", "_uab_collina=167420814657950228155444; _gcl_au=1.1.1715262094.1674208153; _gid=GA1.2.1644772505.1674208162; _ga_R9YPR75N68=GS1.1.1674208154.1.1.1674208666.59.0.0; _ga_65TZCJSDBD=GS1.1.1674207147.1.1.1674208666.0.0.0; _ga=GA1.2.1288971636.1674208154")
req.Header.Set("if-modified-since", "Fri, 20 Jan 2023 03:12:08 GMT")
req.Header.Set("if-none-match", `"63ca0688-331c"`)
req.Header.Set("referer", "http://fanyi.caiyunapp.com/")
req.Header.Set("sec-ch-ua", `"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
req.Header.Set("sec-fetch-dest", "document")
req.Header.Set("sec-fetch-mode", "navigate")
req.Header.Set("sec-fetch-site", "same-origin")
req.Header.Set("sec-fetch-user", "?1")
req.Header.Set("upgrade-insecure-requests", "1")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
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)
}
5.打开这个网站,把浏览器中的json字符串粘贴进去,就可以获得对应的go结构体,之后我们利用网站生成的代码和结构体就已经完成了大半了,这时候可以根据自己的需求去调整一下,可以把4中的代码当做一个函数,这样方便每次去查询
实战案例三:SOCKS5 代理
SOCKS5最大好处就是可以绕过防火墙并且兼容任何网络或者接口,