使用ssh包透明处理这个问题的一种方法是通过创建一个带有空闲超时的自定义net.Conn
连接来为您设置截止日期。然而,这将导致连接上的后台读取超时,因此我们需要使用ssh keepalives来保持连接打开状态。根据您的用例,仅使用ssh keepalives作为死连接的警报可能已经足够了。
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.DialTimeout
或net.Dialer
获取连接,使用超时时间对其进行包装,然后将其传递到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)
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
}
Conn
上的SetDeadline()
来执行NewClient()
是正确的方法。我进行评论而不是回答,因为我对ssh
包没有特别的熟悉度。grep -R Deadline src/golang.org/x/crypto/ssh
的输出让我想到该包不会妨碍你,但也不会替你完成;唯一的提及似乎与 ssh 端口转发有关。 - twotwotwoSetDeadline()
是有效的,但我必须说,一个旨在建立连接的库中缺少超时等类似功能似乎是一个重大疏忽。并不是要表现得不感激什么的,也许如果我有时间,我会看看是否可以帮助包含它。 - user3591723ssh.NewClientConn
根本没有返回nil客户端的错误。这就是我的主要问题所在,它试图使用一个死掉的服务器调用NewSession
,而这个服务器被传递为活动状态。 - user3591723ssh.NewClientConn
返回ok,则服务器并非真正死亡,因为它可以完成ssh握手。 - JimBssh.NewClientConn
的某些内容不一定能成功启动新会话,这对我来说似乎是奇怪的行为。当我尝试StartSession
时遇到ssh:unexpected packet in response to channel open:<nil>
时,如果我回头查看ssh.NewClientConn
的错误/输出,我肯定已经得到了一个nil
错误。如果那是预期的结果,我认为这没有多大意义。 - user3591723