Golang实现Socks5代理时远程主机TCP连接超时问题
Golang实现Socks5代理时远程主机TCP连接超时问题
我尝试通过SOCKS5代理向远程主机发送请求,但遇到了超时问题。
看起来我设置的超时时间被忽略了,因为应用程序会卡住2分钟。
我有以下代码:
dialer, socks_err := proxy.SOCKS5("tcp", proxyAddr,
nil,
&net.Dialer {
Timeout: time.Second * 5,
},
)
if err != nil {
fmt.Println("can't connect to the proxy:", err)
return
}
connection, err = dialer.Dial("tcp", "google.com:43") // 应用程序在这里卡住,2分钟后出现"i/o timeout"错误
fmt.Println(err)
看起来我设置的超时仅适用于连接到代理服务器,但不适用于连接到远程主机。
如何为远程主机连接/请求设置超时?
谢谢!
更多关于Golang实现Socks5代理时远程主机TCP连接超时问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang实现Socks5代理时远程主机TCP连接超时问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你遇到的问题确实是因为net.Dialer的超时设置只适用于到SOCKS5代理服务器的初始连接,而不适用于通过代理建立的到远程主机的连接。要解决这个问题,你需要为远程主机连接设置独立的超时控制。
以下是几种解决方案:
方案1:使用context.WithTimeout(推荐)
import (
"context"
"net"
"time"
"golang.org/x/net/proxy"
)
func main() {
dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, &net.Dialer{
Timeout: 5 * time.Second,
})
if err != nil {
fmt.Println("can't connect to the proxy:", err)
return
}
// 为远程主机连接设置超时
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var connection net.Conn
connection, err = dialer.(proxy.ContextDialer).DialContext(ctx, "tcp", "google.com:43")
if err != nil {
fmt.Println("connection failed:", err)
return
}
defer connection.Close()
fmt.Println("Connected successfully")
}
方案2:使用net.Dialer包装器
type timeoutDialer struct {
proxy.Dialer
timeout time.Duration
}
func (td *timeoutDialer) Dial(network, addr string) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), td.timeout)
defer cancel()
if contextDialer, ok := td.Dialer.(proxy.ContextDialer); ok {
return contextDialer.DialContext(ctx, network, addr)
}
// 如果代理不支持ContextDialer,使用通道实现超时
result := make(chan struct {
conn net.Conn
err error
})
go func() {
conn, err := td.Dialer.Dial(network, addr)
result <- struct {
conn net.Conn
err error
}{conn, err}
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case res := <-result:
return res.conn, res.err
}
}
// 使用包装器
func main() {
baseDialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, &net.Dialer{
Timeout: 5 * time.Second,
})
if err != nil {
fmt.Println("can't connect to the proxy:", err)
return
}
timeoutDialer := &timeoutDialer{
Dialer: baseDialer,
timeout: 5 * time.Second,
}
connection, err := timeoutDialer.Dial("tcp", "google.com:43")
if err != nil {
fmt.Println("connection failed:", err)
return
}
defer connection.Close()
fmt.Println("Connected successfully")
}
方案3:使用goroutine和select实现超时
func dialWithTimeout(dialer proxy.Dialer, network, addr string, timeout time.Duration) (net.Conn, error) {
result := make(chan struct {
conn net.Conn
err error
})
go func() {
conn, err := dialer.Dial(network, addr)
result <- struct {
conn net.Conn
err error
}{conn, err}
}()
select {
case <-time.After(timeout):
return nil, &net.OpError{
Op: "dial",
Net: network,
Err: &timeoutError{},
}
case res := <-result:
return res.conn, res.err
}
}
type timeoutError struct{}
func (e *timeoutError) Error() string { return "dial timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
// 使用
func main() {
dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, &net.Dialer{
Timeout: 5 * time.Second,
})
if err != nil {
fmt.Println("can't connect to the proxy:", err)
return
}
connection, err := dialWithTimeout(dialer, "tcp", "google.com:43", 5*time.Second)
if err != nil {
fmt.Println("connection failed:", err)
return
}
defer connection.Close()
fmt.Println("Connected successfully")
}
第一种方案是最简洁和推荐的,因为它利用了Go标准库的context机制。确保你的golang.org/x/net/proxy版本支持ContextDialer接口。

