dslijian2015 2015-07-22 05:00
浏览 282
已采纳

SSH连接超时

I am trying to make SSH connections using golang.org/x/crypto/ssh and I am kinda surprised that I can't seem to find out how to timeout the NewSession function (I actually don't seen any way to timeout anything). When I try to connect to a server that is having issues, this just hangs for a very long time. I have written something to use select with a time.After but it just feels like a hack. Something I haven't tried yet is to keep the underlying net.Conn in my struct and just keep doing Conn.SetDeadline() calls. Haven't tried this yet because I don't know if the crypto/ssh library overrides this or anything like that.

Anyone have a good way to timeout dead servers with this library? Or does anyone know of a better library?

  • 写回答

2条回答 默认 最新

  • douoyou3348 2015-07-22 14:38
    关注

    One way to handle this transparently with the ssh package, is to create a connection with an idle timeout via a custom net.Conn which sets deadlines for you. However, this will cause the background Reads on a connection to timeout, so we need to use ssh keepalives to keep the connection open. Depending on your use case, simply using ssh keepalives as an alert for a dead connection may suffice.

    // 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)
    }
    

    You can then use net.DialTimeout or a net.Dialer to get the connection, wrap it in your Conn with timeouts, and pass it into 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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料