基于Go的SSH连接与操作服务器

连接工具

package Tools

import (
    "errors"
    "fmt"
    "github.com/pkg/sftp"
    "golang.org/x/crypto/ssh"
    "io"
    "log"
    "os"
    "time"
)

// 连接的配置
type ClientConfig struct {
    Host       string         //ip
    Port       int            // 端口
    Username   string         //用户名
    Password   string         //密码
    SshClient  *ssh.Client    //ssh client
    Session    *ssh.Session   // ssh session
    Stdin      io.WriteCloser // 获取标准输入
    Stdout     io.Reader      // 获取标准输出
    Stderr     io.Reader      // 获取标准异常
    sftpClient *sftp.Client   //sftp client
    LastResult string         //最近一次运行的结果
}

func (cliConf *ClientConfig) CreateClient(host string, port int, username, password string) error {
    /**
    创建链接
    */
    var (
        sshClient *ssh.Client
        err       error
    )
    cliConf.Host = host
    cliConf.Port = port
    cliConf.Username = username
    cliConf.Password = password

    config := ssh.ClientConfig{
        User:            cliConf.Username,
        Auth:            []ssh.AuthMethod{ssh.Password(password)},
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Timeout:         10 * time.Second,
    }
    addr := fmt.Sprintf("%s:%d", cliConf.Host, cliConf.Port)

    if sshClient, err = ssh.Dial("tcp", addr, &config); err != nil {
        return err
    }
    cliConf.SshClient = sshClient

    // 构建sftpClient
    //var sftpClient *sftp.Client
    //if sftpClient, err = sftp.NewClient(sshClient); err != nil {
    //    log.Fatalln("error occurred:", err)
    //}
    //cliConf.sftpClient = sftpClient

    return err
}

func (cliConf *ClientConfig) RunTerminal() error {
    /**
    开启交互式通道
    */

    //获取session,这个session是用来远程执行操作的
    session, err := cliConf.SshClient.NewSession()
    if err != nil {
        return err
    }

    // 获取标准输入管道
    stdin, err := session.StdinPipe()
    if err != nil {
        return err
    }

    // 获取标准输出管道
    stdout, err := session.StdoutPipe()
    if err != nil {
        return err
    }

    // 获取标准异常管道
    stderr, err := session.StderrPipe()
    if err != nil {
        return err
    }

    // 启动交互式Shell会话
    if err := session.Shell(); err != nil {
        return err
    }

    cliConf.Stdin = stdin
    cliConf.Stdout = stdout
    cliConf.Stderr = stderr
    cliConf.Session = session
    go func() {
        // 等待会话结束
        if err := session.Wait(); err != nil {
            log.Fatalf("Failed to wait for session: %s", err)
        }
    }()

    return nil
}

func (cliConf *ClientConfig) RunCommand(cmd string) error {
    _, errExecute := fmt.Fprintf(cliConf.Stdin, "%s\n", cmd)
    if errExecute != nil {
        return errExecute
    }

    return nil
}

func (cliConf *ClientConfig) RunShell(shell string) (string, error) {
    var (
        session *ssh.Session
        err     error
    )

    //获取session,这个session是用来远程执行操作的
    if session, err = cliConf.SshClient.NewSession(); err != nil {
        log.Fatalln("error occurred:", err)
        return "", err
    }
    //执行shell
    if output, err := session.CombinedOutput(shell); err != nil {
        fmt.Println(shell)
        log.Fatalln("error occurred:", err)
        return "", err
    } else {
        cliConf.LastResult = string(output)
    }
    return cliConf.LastResult, nil
}

func (cliConf *ClientConfig) Upload(srcPath, dstPath string) error {
    /**
    下载文件
    */
    if cliConf.sftpClient == nil {
        return errors.New("no Create sftpClient")
    }
    srcFile, _ := os.Open(srcPath)                   //本地
    dstFile, _ := cliConf.sftpClient.Create(dstPath) //远程
    defer func() {
        _ = srcFile.Close()
        _ = dstFile.Close()
    }()
    buf := make([]byte, 1024)
    for {
        n, err := srcFile.Read(buf)
        if err != nil {
            if err != io.EOF {
                log.Fatalln("error occurred:", err)
                return err
            } else {
                break
            }
        }
        _, _ = dstFile.Write(buf[:n])
    }
    fmt.Println(cliConf.RunShell(fmt.Sprintf("ls %s", dstPath)))
    return nil
}

func (cliConf *ClientConfig) Download(srcPath, dstPath string) error {
    /**
    上传文件
    */
    if cliConf.sftpClient == nil {
        return errors.New("no Create sftpClient")
    }
    srcFile, err := cliConf.sftpClient.Open(srcPath) //远程
    fmt.Println(srcFile)
    if err != nil {
        fmt.Println(err.Error())
        return err
    }

    dstFile, _ := os.Create(dstPath) //本地
    defer func() {
        _ = srcFile.Close()
        _ = dstFile.Close()
    }()

    if _, err := srcFile.WriteTo(dstFile); err != nil {
        log.Fatalln("error occurred", err)
        return err
    }
    fmt.Println("文件下载完毕")

    return nil
}

使用样例

测试连接与执行

type SelfObject struct {
    cliConf     *Tools.ClientConfig
}

func (s *SelfObject) TestDBConnect(ip string, port int, user string, password string) error {
    // 打开ssh连接
    cliConf := new(Tools.ClientConfig)
    err := cliConf.CreateClient(ip, port, user, password)
    if err != nil {
        if strings.Contains(err.Error(), "unable to authenticate") {
            return errors.New("SSH连接的用户名密码错误,请重新确认")
        } else if strings.Contains(err.Error(), " i/o timeout") {
            return errors.New("SSH连接超时,请确认网络是否正常")
        }

        return err
    }

    res, err := cliConf.RunShell("pwd")
    if err != nil {
        return err

    }
    fmt.Println(res)

    return nil
}

交互式窗口连续执行

type SelfObject struct {
    cliConf     *Tools.ClientConfig
}

func (s *SelfObject) RunTerminal(ip string, port int, user string, password string) error {
    // 打开ssh连接
    cliConf := new(Tools.ClientConfig)
    err := cliConf.CreateClient(ip, port, user, password)
    if err != nil {
        if strings.Contains(err.Error(), "unable to authenticate") {
            return errors.New("SSH连接的用户名密码错误,请重新确认")
        } else if strings.Contains(err.Error(), " i/o timeout") {
            return errors.New("SSH连接超时,请确认网络是否正常")
        }

        return err
    }

    // 打开交互式窗口
    err = cliConf.RunTerminal()
    if err != nil {
        fmt.Println("Open Terminal Error:", err)
        return err
    }

    defer cliConf.Session.Close()

    // 读取异常管道
    readErrFlag := false
    go func() {
        scanerr := bufio.NewScanner(cliConf.Stderr)
        for scanerr.Scan() {
            err := errors.New(scanerr.Text())
            defer func() {
                if r := recover(); r != nil {
                    // 将异常传递到外部
                    panic(err)
                }
            }()
            cliConf.Stdin.Close()
        }
    }()

    // 从标准输出管道中读取输出
    go func() {
        defer func() {
            err := recover()
            if err != nil {
                fmt.Println("解析输出协程异常:", err)
            }
        }()

        scanner := bufio.NewScanner(cliConf.Stdout)

        for scanner.Scan() {
            line := scanner.Text()
            fmt.Println(line)
        }
    }()

    cliConf.RunCommand("pwd")
    cliConf.RunCommand("mkdir test")                           
    cliConf.RunCommand("cd mkdir;echo hello")
    cliConf.Stdin.Close() // 关闭会话

    // 等待会话结束
    if err := cliConf.Session.Wait(); err != nil {
        log.Fatalf("Failed to wait for session: %s", err)
    }
}
  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川涂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值