Go中为SSH TCP连接设置空闲超时遇连接关闭问题求助
解决Go语言TCP转SSH连接空闲超时导致SSH直接关闭的问题
问题根源
将自定义Conn移到外部后出现SSH连接直接关闭,大概率是两个原因:一是自定义Conn没有完整实现net.Conn接口,导致SSH握手或传输时出现异常;二是超时逻辑在SSH握手阶段就触发,直接中断了连接建立过程。
具体解决步骤
1. 完整实现带超时的自定义Conn
SSH的NewServerConn依赖完整的net.Conn接口实现,仅重写Read方法远远不够,必须透传所有接口方法,同时在每次读写操作后重置超时计时器,确保空闲计时只针对无数据传输的阶段。示例代码:
import ( "net" "sync" "time" ) type TimeoutConn struct { net.Conn timeout time.Duration mu sync.Mutex timer *time.Timer } func NewTimeoutConn(conn net.Conn, timeout time.Duration) *TimeoutConn { tc := &TimeoutConn{ Conn: conn, timeout: timeout, } tc.resetTimer() return tc } // 重置超时计时器,加锁保证并发安全 func (tc *TimeoutConn) resetTimer() { tc.mu.Lock() defer tc.mu.Unlock() if tc.timer != nil { tc.timer.Stop() } tc.timer = time.AfterFunc(tc.timeout, func() { _ = tc.Conn.Close() }) } // 重写Read,读取成功后重置超时 func (tc *TimeoutConn) Read(b []byte) (n int, err error) { n, err = tc.Conn.Read(b) if err == nil { tc.resetTimer() } return } // 重写Write,写入成功后重置超时 func (tc *TimeoutConn) Write(b []byte) (n int, err error) { n, err = tc.Conn.Write(b) if err == nil { tc.resetTimer() } return } // 重写Close,清理计时器避免资源泄漏 func (tc *TimeoutConn) Close() error { tc.mu.Lock() defer tc.mu.Unlock() if tc.timer != nil { tc.timer.Stop() } return tc.Conn.Close() }
2. 调整超时启用时机,避开SSH握手阶段
如果在SSH握手(ssh.NewServerConn)过程中就启用超时,很可能因为握手耗时超过阈值导致连接被中断。正确的做法是先完成SSH握手,再为已建立的连接添加超时逻辑:
func handleTCPConn(tcpConn net.Conn) { defer tcpConn.Close() // 先完成SSH握手,此时不启用超时 sshConn, chans, reqs, err := ssh.NewServerConn(tcpConn, yourSSHConfig) if err != nil { log.Printf("SSH handshake failed: %v", err) return } defer sshConn.Close() // 握手完成后,包装SSH连接为带超时的Conn timeoutConn := NewTimeoutConn(sshConn, 20*time.Second) // 处理SSH请求和通道 go ssh.DiscardRequests(reqs) for newChan := range chans { if newChan.ChannelType() != "session" { newChan.Reject(ssh.UnknownChannelType, "only session channels are supported") continue } chan_, reqs, err := newChan.Accept() if err != nil { log.Printf("Accept channel failed: %v", err) continue } go handleSSHSession(chan_, reqs, timeoutConn) } }
3. 排查并发安全问题
自定义Conn中的计时器操作必须加锁保护,否则多goroutine同时读写时,可能出现计时器重复Stop/重置的异常,导致连接被误关闭。上面的示例已经通过sync.Mutex保证了计时器操作的原子性。
4. 验证超时逻辑
测试时可以:
- 建立SSH连接后保持空闲20秒,确认连接被正常关闭
- 在连接活跃时持续发送数据,确认超时计时器被正确重置,不会误关闭连接
内容的提问来源于stack exchange,提问作者Sanjay Kumar
相关产品推荐
相关产品推荐

