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

报告相同问题?

悬赏问题

  • ¥35 平滑拟合曲线该如何生成
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
  • ¥50 成都蓉城足球俱乐部小程序抢票
  • ¥15 yolov7训练自己的数据集