1.猜谜游戏
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("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
}
//ReadString返回的结果包含结尾的换行符,将其去掉
input = strings.Trim(input, "\r\n")
//转换为数字
guess, err := strconv.Atoi(input)
//转换失败处理
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
}
}
}
思路:
- 生成随机数
- 读取输入文本
- 删除不必要的换行符
- 转化文本为数字
- 循环判断是否猜数正确
- 正确退出循环
- 不正确则从第二步重新开始
2.词典爬虫
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
// 请求参数结构体
type DictRequest struct {
// 翻译类型
TransType string `json:"trans_type"`
// 翻译文本
Source string `json:"source"`
// 用户id
UserID string `json:"user_id"`
}
type DictResponse struct {
Rc int `json:"rc"`
Wiki struct {
} `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 []interface{} `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 query(word string) {
client := &http.Client{}
// 设置请求参数
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
// 设置参数数据流
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", "f1de93819e3bb9f68a199a51c6ee2efb")
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", `"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"`)
req.Header.Set("sec-ch-ua-mobile", "?1")
req.Header.Set("sec-ch-ua-platform", `"Android"`)
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 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36 Edg/113.0.1774.35")
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)
}
// 防止请求出错
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse DictResponse
// 将响应数据转化为字符串
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
// 循环查找响应数据中的翻译结果
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
func main() {
// 运行代码:go run dict.go hello
// hello 即为要翻译的文本
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
-
在网站输入信息观察网络活动
-
通过浏览器开发者工具复制cURL(bash)
-
利用网站生成代码
-
运行获取响应数据
-
利用网站将响应数据转换为结构体
-
把请求参数封装为结构体
-
把前面生成的请求代码封装改造成方法
-
在main函数中调用方法
示例抓包网站:fanyi.caiyunapp.com/
代码生成网站:
curl命令——json
json——golang struct
3.socks5代理
[ SOCKS5代理实例理解分析 | 青训营笔记 ] - 掘金 (juejin.cn)
1.socks5协议
Socks 协议是一种代理 (Proxy) 协议, 它在使用TCP/IP协议通信的前端机器和服务器之间发挥中介作用,使得内部网中的前端机器能够访问Internet网中的服务器。
例如我们所熟知的 Shdowsocks 便是 Socks 协议的一个典型应用程序, Socks 协议有多个版本, 目前最新的版本为 5, 其协议标准文档为 RFC 1928
2.代理服务器
代理服务器(Proxy Server)的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站,是个人网络和Internet服务商之间的中间代理机构,负责转发合法的网络信息,对转发进行控制和登记
3.socks5代理服务器
主要功能:处于使用TCP/IP协议通信的前端机器,将信息或请求发送给Socks5,再由Socks5发送给服务器,使得内部网中的前端机器能够访问Internet网中的服务器
4.socks5交互原理
-
客户端请求代理连接:客户端通过建立与 SOCKS5 代理服务器的连接来请求代理服务。通常,客户端会在其配置中指定代理服务器的地址和端口。
-
协商认证方式:一旦连接建立,客户端和代理服务器会协商认证方式。SOCKS5 协议支持多种认证方式,包括无需认证、用户名/密码认证等。客户端和代理服务器会选择一种双方都支持的认证方式进行验证。
-
请求目标服务器连接:认证完成后,客户端发送请求给代理服务器,包括目标服务器的地址和端口号。客户端还可以指定是否需要远程解析 DNS,以及是否启用 UDP 代理。
-
代理服务器连接目标服务器:代理服务器使用自己的 IP 地址和端口与目标服务器建立连接。这样,代理服务器充当了客户端和目标服务器之间的中间人。
-
代理数据传输:一旦代理服务器成功建立与目标服务器的连接,它就开始在客户端和目标服务器之间传输数据。客户端发送的数据会经过代理服务器中转,然后代理服务器将响应从目标服务器传回给客户端。
-
连接关闭:当客户端或目标服务器关闭连接时,代理服务器也会关闭与两者的连接。这标志着一次 SOCKS5 代理会话的结束。
5.go语言实现原理
在Go语言中,可以使用net包来实现Socks5代理的功能。通过net.Listen函数可以创建一个Socks5代理服务器,通过net.Dial函数可以创建一个Socks5代理客户端。
当客户端连接到代理服务器时,代理服务器将解析连接请求,验证客户端的身份,然后将请求转发到目标服务器。当目标服务器响应后,代理服务器将解析响应,将响应返回给客户端。
在Go语言实现Socks5代理时,需要使用bufio包来处理I/O缓冲区。bufio.NewReader函数用于创建一个缓冲读取器,可以用来读取从客户端和代理服务器之间传输的数据。bufio.Reader提供了一些方便的方法来读取和操作缓冲区的数据。
在实现Socks5代理时,我们需要处理客户端和代理服务器之间的数据报格式。Socks5代理协议的数据报格式由多个字段组成,每个字段都有特定的含义和格式。具体来说,Socks5代理协议的数据报格式如下:
客户端发送的数据报格式(握手协议):
| VER | NMETHODS | METHODS |
|---|---|---|
| 1 | 1 | 1 to 255 |
代理服务器回复的数据报格式(握手协议):
| VER | METHOD |
|---|---|
| 1 | 1 |
客户端发送的数据报格式(请求协议):
| VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|---|---|---|---|---|---|
| 1 | 1 | 0 | 1 | Variable | 2 |
代理服务器回复的数据报格式(请求协议):
| VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|---|---|---|---|---|---|
| 1 | 1 | 0 | 1 | Variable | 2 |
其中,VER表示协议版本号,NMETHODS表示支持的认证方法数量,METHODS表示支持的认证方法列表。CMD表示请求类型,RSV为保留字段,ATYP表示目标地址类型,DST.ADDR表示目标地址,DST.PORT表示目标端口。REP表示回复类型,BND.ADDR表示代理服务器绑定的地址,BND.PORT表示代理服务器绑定的端口。
使用Go语言实现Socks5代理时,我们可以使用bufio包来处理数据报格式中的各个字段。在建立连接后,我们可以使用bufio.NewReader函数创建一个缓冲读取器来读取客户端发送的数据。通过使用bufio包提供的方便的方法,我们可以轻松地解析Socks5代理协议的数据报格式,并处理代理请求和回复。
总之,Go语言提供了方便易用的bufio包来处理I/O缓冲区,使得在实现Socks5代理协议时能够更加轻松地解析数据报格式。熟练掌握bufio包的使用,可以让我们更加高效地实现Socks5代理协议,并提供高效稳定的代理服务。
6.代码与分析
package main
import (
"bufio"
"context"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
)
const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04
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)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
err = connect(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
}
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |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
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)
}
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}
return nil
}
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// VER 版本号,socks5的值为0x05
// CMD 0x01表示CONNECT请求
// RSV 保留字段,值为0x00
// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
// 0x01表示IPv4地址,DST.ADDR为4个字节
// 0x03表示域名,DST.ADDR是一个可变长度的域名
// DST.ADDR 一个可变长度的值
// DST.PORT 目标端口,固定2个字节
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
ver, cmd, atyp := buf[0], buf[1], buf[3]
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
if cmd != cmdBind {
return fmt.Errorf("not supported cmd:%v", cmd)
}
addr := ""
switch atyp {
case atypeIPV4:
_, 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 = 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)
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// VER socks版本,这里为0x05
// REP Relay field,内容取值如下 X’00’ succeeded
// RSV 保留字段
// ATYPE 地址类型
// BND.ADDR 服务绑定的地址
// BND.PORT 服务绑定的端口DST.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)
cancel()
}()
<-ctx.Done()
return nil
}
这段代码是一个实现 SOCKS5 代理服务器的简单示例。以下是代码的主要功能和流程解释:
- 定义了一些常量,包括 SOCKS5 协议版本、命令类型、地址类型等。
- 在
main函数中,创建一个 TCP 服务器监听在127.0.0.1:1080地址上。 - 通过无限循环接受客户端连接,在每个连接上调用
process函数进行处理。 process函数处理每个客户端连接的逻辑。它首先读取客户端发送的认证请求,并根据协议规范进行认证。auth函数处理认证过程。它首先读取客户端发送的协议版本,然后读取支持的认证方法数量和方法列表。根据规范,服务器返回支持的认证方法给客户端。- 客户端在收到认证响应后,可以选择其中一种认证方法进行身份验证。
- 客户端完成认证后,调用
connect函数建立与目标服务器的连接。 connect函数读取客户端发送的连接请求,并解析目标地址和端口。- 根据解析得到的目标地址和端口,建立与目标服务器的连接。
- 客户端和目标服务器之间的数据传输通过两个 goroutine 实现:一个从客户端读取数据并发送到目标服务器,另一个从目标服务器读取数据并发送到客户端。
- 使用上下文
ctx和取消函数cancel来控制数据传输的生命周期。 - 当其中一个 goroutine 完成数据传输或发生错误时,取消函数被调用,通知另一个 goroutine 停止数据传输。
- 最后,函数等待上下文的完成信号,结束处理。
总体而言,这段代码实现了一个简单的 SOCKS5 代理服务器,它可以接受客户端连接并转发流量到目标服务器。