我正在try 使用golang.org/x/crypto/ssh建立SSH连接,我有点惊讶的是,我似乎找不到如何使NewSession函数超时的方法(实际上,我没有看到任何方法来超时任何东西).当我try 连接到出现问题的服务器时,它会挂起很长一段时间.我已经写了一些使用selecttime.After的东西,但感觉就像是黑客攻击.我还没有try 的一件事是将底层的net.Conn保留在我的 struct 中,并继续执行Conn.SetDeadline()个调用.我还没有try 过这一点,因为我不知道crypto/ssh库是否覆盖了这个或类似的东西.

有没有人有好办法让这个库停用的服务器超时?或者有没有人知道更好的图书馆?

推荐答案

使用ssh包透明地处理这个问题的一种方法是,通过定制的net.Conn创建一个空闲超时的连接,为您设置截止日期.然而,这将导致连接上的后台读取超时,因此我们需要使用ssh keepalives来保持连接打开.根据您的使用情况,简单地使用ssh keepalives作为死机连接的alert 可能就足够了.

// Conn wraps a net.Conn, and sets a deadline for every read
// and write operation.
type Conn struct {
    net.Conn
    ReadTimeout  time.Duration
    WriteTimeout time.Duration
}

func (c *Conn) Read(b []byte) (int, error) {
    err := c.Conn.SetReadDeadline(time.Now().Add(c.ReadTimeout))
    if err != nil {
        return 0, err
    }
    return c.Conn.Read(b)
}

func (c *Conn) Write(b []byte) (int, error) {
    err := c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
    if err != nil {
        return 0, err
    }
    return c.Conn.Write(b)
}

然后,你可以使用net.DialTimeoutnet.Dialer来获得连接,将其包装在带有超时的Conn中,然后将其传递到ssh.NewClientConn.

func SSHDialTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
    conn, err := net.DialTimeout(network, addr, timeout)
    if err != nil {
        return nil, err
    }

    timeoutConn := &Conn{conn, timeout, timeout}
    c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config)
    if err != nil {
        return nil, err
    }
    client := ssh.NewClient(c, chans, reqs)

    // this sends keepalive packets every 2 seconds
    // there's no useful response from these, so we can just abort if there's an error
    go func() {
        t := time.NewTicker(2 * time.Second)
        defer t.Stop()
        for range t.C {
            _, _, err := client.Conn.SendRequest("keepalive@golang.org", true, nil)
            if err != nil {
                return
            }
        }
    }()
    return client, nil
}

Go相关问答推荐

Go 1.22 http mux:在同一路径上提供一个手柄和一个FS

Go GORM创建表,但不创建列

如何在VSCode中为特定的.go文件创建调试配置?

如何修复proxyconnect tcp:tls:第一条记录看起来不像tls握手

未实现的 desc = 未知服务 pb.AuthService 我的简单身份验证服务器上出现错误

在golang二进制中嵌入SvelteKit

golang gin 获取 cookie json

io.Reader 无限循环与 fmt.Fscan

有没有什么方法可以在不使用 if/else 的情况下在 Golang 中处理 nil 指针?

CBC Decrypter 解密加密文本,但部分文本被随机字符替换

Go gmail api 快速入门导致本地主机拒绝连接 ERR_CONNECTION_REFUSED

查找、解析和验证邮箱地址

Golang Gin 绑定请求正文 XML 到 Slice

Go:等待多个通道的性能损失

当 git clone 工作时,Go mod tidy 在私有存储库上失败

如何在gorm中处理多个查询

正确编码 JWT

如何扩充 ResponseWriter 的 Header() 返回的 map

如何将类型转换为字节数组golang

AWS EKS 上的 Golang REST API 部署因 CrashLoopBackOff 而失败