如何使用 tls.Dial 添加 10 秒超时? (没有tls.DialTimeout对应net.DialTimeout)

Posted

技术标签:

【中文标题】如何使用 tls.Dial 添加 10 秒超时? (没有tls.DialTimeout对应net.DialTimeout)【英文标题】:How can I add a 10 second timeout with tls.Dial ? (There is no tls.DialTimeout to correspond to net.DialTimeout) 【发布时间】:2021-11-20 00:56:27 【问题描述】:

在 Go 中使用 tls.Dial 时添加超时的最佳方法是什么?

我看到net包有net.DialTimeout,可惜tls包没有对应的功能。

我想我应该使用contextDialer 来实现超时,但我不是围棋专家,我找不到任何好的例子。

(1) 我找到了tls.DialWithDialer,但我不确定如何创建一个配置了超时的net.Dialer

func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error)

(2) 我也找到了tls.DialContext,但我不确定如何使用它来实现超时。

func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error)

(3) 我认为可以使用 net.DialTimeout 建立初始连接,然后升级连接并继续 TLS 握手,但我找不到任何示例说明如何做到这一点。

任何帮助或指导将不胜感激。

这是我的简单程序,它连接到服务器列表并打印有关证书链的一些信息。当服务器没有响应时,该程序会挂起很长时间。我只想在 10 秒后超时。

package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "os"
)

func main() 
    port := "443"
    conf := &tls.Config
        InsecureSkipVerify: true,
    
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() 
        host := s.Text()
        conn, err := tls.Dial("tcp", host+":"+port, conf)
        if err != nil 
            fmt.Println("Host:", host, "Dial:", err)
            continue
        
        defer conn.Close()
        certs := conn.ConnectionState().PeerCertificates
        for _, cert := range certs 
            fmt.Println("Host:", host, "Issuer:", cert.Issuer)
        
    

【问题讨论】:

【参考方案1】:

正如您在问题中提到的,有几个选项;使用DialContext 是一种常用技术:

package main

import (
    "bufio"
    "context"
    "crypto/tls"
    "fmt"
    "os"
    "time"
)

func main() 
    port := "443"
    conf := &tls.Config
        InsecureSkipVerify: true,
    
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() 
        host := s.Text()
        ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
        d := tls.Dialer
            Config: conf,
        
        conn, err := d.DialContext(ctx,"tcp", host+":"+port)
        cancel() // Ensure cancel is always called
        if err != nil 
            fmt.Println("Host:", host, "Dial:", err)
            continue
        

        // warning: using defer in a loop may not have the expected result
        // the connection will remain open until the function exists
        defer conn.Close()

        tlsConn := conn.(*tls.Conn)
        certs := tlsConn.ConnectionState().PeerCertificates
        for _, cert := range certs 
            fmt.Println("Host:", host, "Issuer:", cert.Issuer)
        
    

使用上述方法可以相对简单地让您的代码的用户取消请求(接受context 并在上面有context.Background() 的地方使用它)。如果这对您不重要,那么使用 DialerTimeout 会更简单:

conn, err := tls.DialWithDialer(&net.DialerTimeout:  10 * time.Second, "tcp", host+":"+port, conf)
if err != nil  
    fmt.Println("Host:", host, "Dial:", err)
    continue


certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs 
    fmt.Println("Host:", host, "Issuer:", cert.Issuer)

conn.Close()

【讨论】:

以上是关于如何使用 tls.Dial 添加 10 秒超时? (没有tls.DialTimeout对应net.DialTimeout)的主要内容,如果未能解决你的问题,请参考以下文章

向 DatagramSocket 添加超时-receive()

Zapier频繁10.01秒超时

具有超时条件的 XMLHTTPRequest

如何使用 NSURLConnection (NSMutableRequest) 超时

如何使用 python 烧瓶防止 230 秒 azure 网关超时以进行长时间运行的工作负载

为什么SO_LINGER选项没有0超时或10秒超时没有立即删除套接字或10秒后?