You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go SFTP服务器在单个客户端断开后退出的问题解决

让Go SFTP服务器支持多连接的解决方案

问题核心是原示例仅单次接受并处理连接,单个客户端断开后程序就会终止。要实现多连接支持,只需做两个关键调整:

1. 循环监听新连接

将原来单次调用listener.Accept()的逻辑放入for循环,让服务器持续等待新的客户端连接请求。

2. 用goroutine异步处理每个连接

把单个连接的认证、SFTP服务启动逻辑放到独立goroutine中执行,避免主goroutine被单个连接的处理流程阻塞,确保能持续接受下一个连接。

修改后的核心代码示例

func main() {
    // 保留原有的密钥加载、SSH ServerConfig配置代码...

    listener, err := net.Listen("tcp", ":2022")
    if err != nil {
        log.Fatalf("unable to listen on :2022: %v", err)
    }
    defer listener.Close()
    log.Print("sftp server listening on :2022")

    // 循环接受新连接
    for {
        nConn, err := listener.Accept()
        if err != nil {
            log.Printf("failed to accept connection: %v", err)
            continue
        }
        log.Printf("new connection from %s", nConn.RemoteAddr())

        // 启动goroutine处理单个连接,不阻塞主循环
        go handleSFTPConnection(nConn, config)
    }
}

// 单独提取连接处理函数,封装单个客户端的逻辑
func handleSFTPConnection(conn net.Conn, config *ssh.ServerConfig) {
    // SSH握手建立连接
    sshConn, chans, reqs, err := ssh.NewServerConn(conn, config)
    if err != nil {
        log.Printf("failed to handshake: %v", err)
        conn.Close()
        return
    }
    defer sshConn.Close()
    log.Printf("new SSH connection from %s (%s)", sshConn.RemoteAddr(), sshConn.User())

    // 忽略全局请求
    go ssh.DiscardRequests(reqs)

    // 处理SFTP会话通道
    for newChannel := range chans {
        if newChannel.ChannelType() != "session" {
            newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
            continue
        }
        channel, requests, err := newChannel.Accept()
        if err != nil {
            log.Printf("could not accept channel: %v", err)
            continue
        }

        // 响应SFTP子系统请求
        go func(in <-chan *ssh.Request) {
            for req := range in {
                req.Reply(req.Type == "subsystem", nil)
            }
        }(requests)

        // 启动SFTP服务实例
        server, err := sftp.NewServer(channel)
        if err != nil {
            log.Printf("failed to start sftp server: %v", err)
            channel.Close()
            return
        }
        // 处理服务生命周期
        if err := server.Serve(); err == io.EOF {
            server.Close()
            log.Print("sftp client exited session.")
        } else if err != nil {
            log.Printf("sftp server error: %v", err)
        }
        channel.Close()
    }
}

关键修改说明

  • 循环Accept:主函数的for循环会持续调用listener.Accept(),除非主动关闭监听器,否则服务器会一直处于监听状态。
  • goroutine隔离:每个新连接都交给独立goroutine处理,主goroutine立刻回到循环等待下一个连接,实现多客户端并发处理。
  • 资源清理:每个连接的处理逻辑中都保留了defer关闭连接的代码,确保客户端断开后资源能被正确释放。

修改完成后,SFTP服务器即可同时处理多个客户端连接,单个客户端断开不会导致服务器终止。

内容的提问来源于stack exchange,提问作者Spacebrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:45:07