通过ssh软件包透明地处理这种情况的一种方法是通过自定义net.Conn
创建一个空闲超时连接,该连接为您设置了最后期限。但是,这会导致连接的后台读取超时,所以我们需要使用ssh keepalives来保持连接处于打开状态。根据您的使用情况,仅使用ssh keepalives作为死连接的警报就足够了。
// 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.DialTimeout
或net.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 {
<-t.C
_, _, err := client.Conn.SendRequest("[email protected]", true, nil)
if err != nil {
return
}
}
}()
return client, nil
}
是的,我认为使用'Conn'' SetDeadline()'做'NewClient()'是Way(tm)。评论不回答'因为我对ssh'软件包没有特别的了解。 'grep -R Deadline src/golang.org/x/crypto/ssh'的输出让我觉得这个包不会妨碍你,但不会为你做;唯一的提及与ssh端口转发相关。 – twotwotwo
是''SetDeadline()'的作品,但我得说一些类似超时缺失的图书馆意味着作出连接似乎是一个主要的疏忽..不要发出忘恩负义或任何东西,也许如果我有时间,我会看到如果我可以帮忙包括它 – user3591723
对我来说有点陌生的事情是,当我建立一个连接到我认识的一台服务器时,我知道已经死亡/有问题(我无法从终端进入ssh)我根本不会从ssh.NewClientConn中得到错误,也不会返回一个零客户端。无论如何,这是我的问题的主要原因,它试图用一个活着传递的死亡服务器调用'NewSession'。 – user3591723