[ Go语言实战 | 青训营笔记]

89 阅读5分钟

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

Go语言实战

1.猜字谜游戏

生成随机数

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	maxNum := 100
	secretNum := rand.Intn(maxNum)
	fmt.Println("The secret number is ", secretNum)

}

执行结果:

始终是81结果需要对其进行改进(设置随机数种子)

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	maxNum := 100
	//设置随机数种子
	rand.Seed(time.Now().UnixNano())
	secretNum := rand.Intn(maxNum)
	fmt.Println("The secret number is ", secretNum)

}

读取用户输入

通过使用reader流进行读取用户的输入,同时以换行符为分界完成对输入筛选,同时进行分割,进行转为整数

package main

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

func main() {
	maxNum := 100
	//设置随机数种子
	rand.Seed(time.Now().UnixNano())
	secretNum := rand.Intn(maxNum)
	fmt.Println("The secret number is ", secretNum)

	fmt.Println("Please input your guess: ")
	//使用流处理
	reader := bufio.NewReader(os.Stdin)
	//读取一行输入
	input, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("error occur", err)
		return
	}
	//去掉换行符
	input = strings.TrimSuffix(input, "\n")

	//转为整形
	guess, err := strconv.Atoi(input)
	if err != nil {
		fmt.Println("error guesss input", err)
		return
	}
	fmt.Println("your guess:", guess)
}

对其添加相应的猜字谜循环代码,完成逻辑操作

func main() {
	maxNum := 100
	//设置随机数种子
	rand.Seed(time.Now().UnixNano())
	secretNum := rand.Intn(maxNum)
	fmt.Println("The secret number is ", secretNum)

	fmt.Println("Please input your guess: ")
	//使用流处理
	reader := bufio.NewReader(os.Stdin)
	for {
		//读取一行输入
		input, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println("error occur", err)
			//修改为continue
			continue
		}
		//去掉换行符
		input = strings.TrimSuffix(input, "\n")

		//转为整形
		guess, err := strconv.Atoi(input)
		if err != nil {
			fmt.Println("error guesss input", err)
			continue
		}
		fmt.Println("your guess:", guess)
		//完成比大小
		
		if guess < secretNum {
			fmt.Println("your guess number is smaller")
		} else if guess > secretNum {
			fmt.Println("your guess number is bigger")
		} else {
			fmt.Println("Correct,you legend!")
			break;
		}
	}	
}
The secret number is  26

Please input your guess: 

20

your guess: 20


your guess number is smaller

21

your guess: 21

your guess number is smaller

22

your guess: 22

your guess number is smaller

23

your guess: 23

your guess number is smaller

24

your guess: 24

your guess number is smaller

25

your guess: 25

your guess number is smaller

26

your guess: 26

Correct,you legend!

在线词典介绍


package main

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

type AutoGenerated struct {
	Rc   int `json:"rc"`
	Wiki struct {
		KnownInLaguages int `json:"known_in_laguages"`
		Description     struct {
			Source string      `json:"source"`
			Target interface{} `json:"target"`
		} `json:"description"`
		ID   string `json:"id"`
		Item struct {
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL  string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink  string `json:"sitelink"`
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"good","user_id":"6327e3ab1dafa9001339ea84"}`)
	//创建请求头
	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="99", "Microsoft Edge";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", "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/109.0.0.0 Safari/537.36 Edg/109.0.1518.52")
	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)

	var dictResponse AutoGenerated
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		fmt.Println("error :", err)
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)
        fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}

Socks5代理服务器

Socks5原理图如下:

image.png

实现步骤

1.TCP echo server 服务器搭建

实现的功能:发送什么,我就回复什么

代码实现:

package main

import (
	"bufio"
	"log"
	"net"
)

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	// err
	if err != nil {
		panic(err)
	}

	// while
	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
		}
	}
}

测试:

mine:project/ $ nc 127.0.0.1  1080
hello
hello

第一阶段认证阶段:

代码如下:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
)

const sock5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	// err
	if err != nil {
		panic(err)
	}

	// while
	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)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != sock5Ver {
		return fmt.Errorf("not supported ver : %w", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil{
		return fmt.Errorf("read method failed : %w",err)
	}
	log.Println("ver",ver,"method",method)

	_,err = conn.Write([]byte{sock5Ver,0x00})

	if err != nil{
		return fmt.Errorf("write failed : %w",err)
	}
	return nil
}

测试结果:

2023/01/19 20:34:19 ver 5 method [0 1]
2023/01/19 20:34:19 auth success

mine:project/ $ curl --socks5 127.0.0.1:1080 -v http://www.qq.com
*   Trying 127.0.0.1:1080...
* TCP_NODELAY set
* SOCKS5 communication to www.qq.com:80
* SOCKS5 connect to IPv4 101.91.42.232:80 (locally resolved)
* Failed to receive SOCKS5 connect request ack.
* Closing connection 0
curl: (7) Failed to receive SOCKS5 connect request ack. 

请求阶段,获取要访问的连接的ip和端口

package main

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const sock5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	// err
	if err != nil {
		panic(err)
	}

	// while
	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)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v out failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != sock5Ver {
		return fmt.Errorf("not supported ver : %w", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed : %w", err)
	}
	log.Println("ver", ver, "method", method)

	_, err = conn.Write([]byte{sock5Ver, 0x00})

	if err != nil {
		return fmt.Errorf("write failed : %w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("reader header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != sock5Ver {
		return fmt.Errorf("not support ver : %w", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not support cmd : %w", cmd)
	}
	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("host size 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 : not support now")
	default:
		return errors.New("unkown atyp")
	}

	_,err = io.ReadFull(reader,buf[:2])
	if err != nil{
		return fmt.Errorf("read port failed : %w",err)
	}
	port := binary.BigEndian.Uint16(buf[:2])
	log.Println("dial",addr,port)

	//给与回复
	_,err = conn.Write([]byte{0x05,0x00,0x00,0x01,0,0,0,0,0,0})
	if err != nil{
		return fmt.Errorf("write failed : %w",err)
	}
	return nil
}

测试:

2023/01/19 22:35:21 ver 5 method [0 1]
2023/01/19 22:35:21 auth success
2023/01/19 22:35:21 dial 101.91.42.232 80

根据上述的结果可以看到,系统完成了获取到了访问的ip地址和端口


mine:project/ $ curl --socks5 127.0.0.1:1080 -v http://www.qq.com
*   Trying 127.0.0.1:1080...
* TCP_NODELAY set
* SOCKS5 communication to www.qq.com:80
* SOCKS5 connect to IPv4 101.91.42.232:80 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080 (#0)
> GET / HTTP/1.1
> Host: www.qq.com
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Empty reply from server
* Connection #0 to host 127.0.0.1 left intact
curl: (52) Empty reply from server

最后一部代理阶段的relay阶段

需要和真正的服务器建立TCP连接

最终代码如下:

package main

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const sock5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	// err
	if err != nil {
		panic(err)
	}

	// while
	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)
	err := auth(reader, conn)
	if err != nil {
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	}
	log.Println("auth success")
	err = connect(reader, conn)
	if err != nil {
		log.Printf("client %v out failed:%v", conn.RemoteAddr(), err)
		return
	}
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	ver, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read ver failed:%w", err)
	}
	if ver != sock5Ver {
		return fmt.Errorf("not supported ver : %w", ver)
	}
	methodSize, err := reader.ReadByte()
	if err != nil {
		return fmt.Errorf("read methodSize failed:%w", err)
	}
	method := make([]byte, methodSize)
	_, err = io.ReadFull(reader, method)
	if err != nil {
		return fmt.Errorf("read method failed : %w", err)
	}
	log.Println("ver", ver, "method", method)

	_, err = conn.Write([]byte{sock5Ver, 0x00})

	if err != nil {
		return fmt.Errorf("write failed : %w", err)
	}
	return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	buf := make([]byte, 4)
	_, err = io.ReadFull(reader, buf)
	if err != nil {
		return fmt.Errorf("reader header failed:%w", err)
	}
	ver, cmd, atyp := buf[0], buf[1], buf[3]
	if ver != sock5Ver {
		return fmt.Errorf("not support ver : %w", ver)
	}
	if cmd != cmdBind {
		return fmt.Errorf("not support cmd : %w", cmd)
	}
	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("host size 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 : not support now")
	default:
		return errors.New("unkown atyp")
	}

	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed : %w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
	if err != nil {
		return fmt.Errorf("dial dst failed : %w", err)
	}
	//关闭连接协议
	defer dest.Close()
	log.Println("dial", addr, port)

	//给与回复
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	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)
	}()
	//等待执行完成
	<-ctx.Done()

	return nil
}


测试结果:

运行后:

mine:project/ $ curl --socks5 127.0.0.1:1080 -v http://www.qq.com
*   Trying 127.0.0.1:1080...
* TCP_NODELAY set
* SOCKS5 communication to www.qq.com:80
* SOCKS5 connect to IPv4 101.91.22.57:80 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080 (#0)
> GET / HTTP/1.1
> Host: www.qq.com
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Moved Temporarily
< Server: stgw
< Date: Thu, 19 Jan 2023 15:03:01 GMT
< Content-Type: text/html
< Content-Length: 137
< Connection: keep-alive
< Location: https://www.qq.com/
< 
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>stgw</center>
</body>
</html>
* Connection #0 to host 127.0.0.1 left intact

测试结果:

`2023/01/19 23:03:01 ver 5 method [0 1]

2023/01/19 23:03:01 auth success

2023/01/19 23:03:01 dial 101.91.22.57 80`