这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
猜字游戏
步骤1:定义最大数字,使用时间戳生成随机数种子,生成随机数
步骤2:使用fmt.Scanf("%d",&guess)在控制台输入我们猜的数
步骤3:如果有错误,则输出数字有误,如果没有错误,判断guess和答案是否相等,如果相等则break,如果guess大于答案则提示guess大于答案,小于也一样。最后再增加一个循环使得玩家不断猜字,直到猜到数字退出。
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)
continue
}
input = strings.TrimSuffix(input, "\r\n")
guess, err := strconv.Atoi(input)*/
var guess int
_, err := fmt.Scanf("%d", &guess)
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
}
}
}
翻译
1.抓包 ai.youdao.com/product-fan…
以CURL(bash)格式复制本次请求的内容,并到curlconverter.com/#go生成go语言代码
数据的返回格式为q=word&from=Auto&to=Auto不需要生成DictRequest结构体
2.生成DictResponse 结构体
复制响应中的数据到oktools.net/json2go 进行转换
创建DictResponse 结构体 并将json数据反序列化
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
输出
fmt.Println(word + "翻译:")
for _, item := range dictResponse.Translation {
fmt.Println(item)
}
实现socks5代理服务器
SOCKS5 是一个代理协议,它在使用[TCP/IP协议]通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网)中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
1.生成一个TCP服务器
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.认证阶段 接收version和methodSize
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)
}
创建一个切片读取mothod
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
返回一个00 response
_, err = conn.Write([]byte{socks5Ver, 0x00})
第三步 请求阶段 (1)获取rearder的内容并存到buf中
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
ver, cmd, atyp := buf[0], buf[1], buf[3]
(2)如果ver不是socks5以及cmd不是0x01则返回不支持的错误
(3)根据atyp选择不同方法获取addr
switch atyp {
case atypIPV4:
_, 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 = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
第一个是版本号,第二个是返回的类型,第三个是保留字段,第四个是atyp
第四步 真正与端口建立连接,双向转发数据 标准库中的io.copy只能实现单向传输,所以我们需要使用两个io.copy.由于connect函数会立即返回,但我们需要等待某一个copy没有数据传输后关闭这时可以使用context.WithCancel实现<-ctx.Done()需要等待cancel的执行才会返回
if err != nil {
return fmt.Errorf("write failed: %w", err)
}
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