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条)

报告相同问题?

悬赏问题

  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?