├── .gitignore
├── README.md
├── config.json
├── go.mod
├── go.sum
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Go template
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | .idea/
25 |
26 | *.log
27 |
28 | *.sh
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ProxyServer
2 |
3 | 将API代理构建成隧道代理池
4 |
5 |
6 |
7 | ## 介绍
8 | 此项目用于将API代理构建成隧道代理池,即通过一个IP和端口来自动随机取出代理池中的代理。
9 |
10 | API代理即:
11 | * 通过API来获取代理,返回值是代理列表包含1~n个代理
12 | * 例如:`[{"ip":"xxx.xxx.xxx.xxx","port":xx},{"ip":"xxx.xxx.xxx.xxx","port":xx}]`
13 |
14 | 这样的代理不方便利用,在代码中通常需要二次操作,这个项目便能完美解决这个问题。
15 |
16 |
17 |
18 | ## 安装
19 | 1.克隆本项目
20 | ```bash
21 | git clone https://github.com/ThinkerWen/ProxyServer.git
22 | ```
23 | 2.修改`main.go`文件中的`getProxies()`函数,将它改为您的API代理的获取函数即可。(项目中的示例使用的是[小象代理](https://www.xiaoxiangdaili.com/)
24 |
25 | ```bash
26 | go get ProxyServer # 下载依赖项
27 | go build ProxyServer # 编译可执行程序
28 | ./ProxyServer # 运行
29 | ```
30 |
31 |
32 |
33 | ## 使用
34 | 在后台启动本项目后,只需配置代理地址为`127.0.0.1:12315`即可(在[配置文件](#配置文件)中可修改),以下用Python做示例:
35 | ```python
36 | import requests
37 |
38 | proxies = {
39 | "http": "http://127.0.0.1:12315",
40 | "https": "http://127.0.0.1:12315"
41 | }
42 | requests.get("http://example.com", proxies=proxies)
43 | ```
44 |
45 |
46 |
47 | ## 配置文件
48 | 配置文件一般默认即可
49 | ```json
50 | {
51 | "bind_ip": "", # 代理服务绑定IP
52 | "bind_port": 12315, # 代理服务绑定端口
53 | "proxy_max_retry": 10, # 代理IP重试次数(超过后此次请求废弃)
54 | "proxy_expire_time": 50, # 代理IP过期时间(超过后此IP移除代理池)
55 | "proxy_pool_length": 50, # 代理IP池大小
56 | "proxy_connect_time_out": 1, # 代理IP连接超时时间(超过后此IP移除代理池)
57 | "check_proxy_time_period": 10, # 代理IP有效性检测间隔
58 | "refresh_proxy_time_period": 10 # API代理的获取间隔
59 | }
60 | ```
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bind_ip": "",
3 | "bind_port": 12315,
4 | "proxy_max_retry": 10,
5 | "proxy_expire_time": 50,
6 | "proxy_pool_length": 50,
7 | "proxy_connect_time_out": 1,
8 | "check_proxy_time_period": 10,
9 | "refresh_proxy_time_period": 10
10 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module ProxyServer
2 |
3 | go 1.19
4 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThinkerWen/ProxyServer/381aee4756328184c8685bca07eb24036a24cead/go.sum
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "log"
8 | "math/rand"
9 | "net"
10 | "net/http"
11 | "os"
12 | "strconv"
13 | "strings"
14 | "sync"
15 | "time"
16 | )
17 |
18 | var (
19 | config Config
20 | mu sync.Mutex
21 | proxyList []string
22 | freeIndex chan int
23 | ProxyApiUrl = "https://api.xiaoxiangdaili.com/ip/get?appKey=956913709144756224&appSecret=eTDUzAY1&cnt=1&wt=json"
24 | )
25 |
26 | type Config struct {
27 | BindIP string `json:"bind_ip"`
28 | BindPort int `json:"bind_port"`
29 | ProxyMaxRetry int `json:"proxy_max_retry"`
30 | ProxyExpireTime int64 `json:"proxy_expire_time"`
31 | ProxyPoolLength int `json:"proxy_pool_length"`
32 | ProxyConnectTimeOut int `json:"proxy_connect_time_out"`
33 | CheckProxyTimePeriod int `json:"check_proxy_time_period"`
34 | RefreshProxyTimePeriod int `json:"refresh_proxy_time_period"`
35 | }
36 |
37 | func init() {
38 | jsonBytes, err := os.ReadFile("config.json")
39 | if err != nil {
40 | log.Fatal("Error reading JSON file:", err)
41 | }
42 | if err = json.Unmarshal(jsonBytes, &config); err != nil {
43 | log.Fatal("Error decoding JSON:", err)
44 | }
45 | proxyList = make([]string, config.ProxyPoolLength)
46 | freeIndex = make(chan int, config.ProxyPoolLength)
47 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
48 | }
49 |
50 | func checkProxy(proxy string) bool {
51 | proxyParts := strings.Split(proxy, ":")
52 |
53 | conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", proxyParts[0], proxyParts[1]), time.Second*time.Duration(config.ProxyConnectTimeOut))
54 | if err != nil {
55 | return false
56 | }
57 | defer func(conn net.Conn) { _ = conn.Close() }(conn)
58 | return true
59 | }
60 |
61 | func setProxyList(pl []string) {
62 | if len(pl) == 0 {
63 | return
64 | }
65 | for _, proxy := range pl {
66 | select {
67 | case index := <-freeIndex:
68 | log.Printf("add proxy %v\n", proxy)
69 | proxyList[index] = fmt.Sprintf("%s#%d", proxy, time.Now().Unix()+config.ProxyExpireTime)
70 | case <-time.NewTimer(time.Microsecond * 500).C:
71 | log.Println("Proxy is full")
72 | return
73 | }
74 | }
75 | }
76 |
77 | func getProxies() {
78 | if len(freeIndex) == 0 {
79 | return
80 | }
81 |
82 | response, err := http.Get(ProxyApiUrl)
83 | if err != nil {
84 | return
85 | }
86 | defer func(Body io.ReadCloser) { _ = Body.Close() }(response.Body)
87 | body, err := io.ReadAll(response.Body)
88 | if err != nil {
89 | return
90 | }
91 | var mapResult map[string]interface{}
92 | if err = json.Unmarshal(body, &mapResult); err != nil {
93 | log.Printf("JsonToMapDemo err: %v\n", err)
94 | return
95 | }
96 | if int(mapResult["code"].(float64)) == 200 {
97 | result := mapResult["data"]
98 | proxies := make([]string, 0)
99 | for _, proxyDict := range result.([]interface{}) {
100 | proxy := proxyDict.(map[string]interface{})
101 | proxies = append(proxies, fmt.Sprintf("%s:%d", proxy["ip"].(string), int(proxy["port"].(float64))))
102 | }
103 | setProxyList(proxies)
104 | }
105 | }
106 |
107 | func getRandomProxy() string {
108 | for i := 0; i < 10; i++ {
109 | if proxy := proxyList[rand.Intn(config.ProxyPoolLength)]; proxy != "" {
110 | return strings.Split(proxy, "#")[0]
111 | }
112 | }
113 | for _, proxy := range proxyList {
114 | if proxy != "" {
115 | return strings.Split(proxy, "#")[0]
116 | }
117 | }
118 | return ""
119 | }
120 |
121 | func forward(conn net.Conn, remote string, retry int) {
122 | client, err := net.DialTimeout("tcp", remote, time.Second*time.Duration(config.ProxyConnectTimeOut))
123 | if err != nil {
124 | retry--
125 | log.Printf("Dial failed: %v", err)
126 | if retry > 0 {
127 | forward(conn, getRandomProxy(), retry)
128 | }
129 | return
130 | }
131 | log.Printf("Forwarding from %v to %v\n", conn.LocalAddr(), client.RemoteAddr())
132 |
133 | ioCopy := func(src net.Conn, dst net.Conn) {
134 | _, _ = io.Copy(src, dst)
135 | _ = src.Close()
136 | }
137 | go ioCopy(conn, client)
138 | go ioCopy(client, conn)
139 | }
140 |
141 | func checkProxyList() {
142 | log.Printf("代理检测中,代理池容量: %d\n", len(proxyList)-len(freeIndex))
143 | for i := 0; i < len(proxyList) && proxyList[i] != ""; i++ {
144 | go func(index int) {
145 | proxy := strings.Split(proxyList[index], "#")[0]
146 | period := strings.Split(proxyList[index], "#")[1]
147 | if period >= strconv.FormatInt(time.Now().Unix(), 10) || checkProxy(proxy) {
148 | mu.Lock()
149 | defer mu.Unlock()
150 | log.Printf("remove inactive proxy %s", proxy)
151 | proxyList[index] = ""
152 | freeIndex <- index
153 | }
154 | }(i)
155 | }
156 | time.Sleep(time.Second * time.Duration(config.CheckProxyTimePeriod))
157 | checkProxyList()
158 | }
159 |
160 | func main() {
161 | for i := 0; i < config.ProxyPoolLength; i++ {
162 | freeIndex <- i
163 | }
164 |
165 | go checkProxyList()
166 | go func() {
167 | for {
168 | getProxies()
169 | time.Sleep(time.Duration(config.RefreshProxyTimePeriod) * time.Second)
170 | }
171 | }()
172 |
173 | listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.BindIP, config.BindPort))
174 | if err != nil {
175 | log.Fatalf("Failed to setup listener: %v", err)
176 | }
177 |
178 | for {
179 | conn, err := listener.Accept()
180 | if err != nil {
181 | log.Fatalf("ERROR: failed to accept listener: %v", err)
182 | }
183 | log.Printf("Accepted connection from %v\n", conn.RemoteAddr().String())
184 | px := getRandomProxy()
185 | if px == "" {
186 | px = "127.0.0.1:60001"
187 | }
188 | go forward(conn, px, config.ProxyMaxRetry)
189 | }
190 | }
191 |
--------------------------------------------------------------------------------