2019独角兽企业重金招聘Python工程师标准>>>

热身运动?

在开始之前,先来个热身运动。虽然标题党写着快速打造一个ssh客户端,但是和跑步一样,在运动前还是需要先热身一下,不然到时候身体(大脑)会吃不消。所以,在开始前,我们先来科普一下ssh的一些东西。

先来说说ssh,这里的ssh是指由IETF的网络小组(Network Working Group)所制定的为建立在应用层和传输层基础上的安全协议。(对于了解这个协议的请忽略本段文字?)点这里了解更多ssh介绍

写过java web应用的同学应该还知道另一个ssh(struts+spring+hibernate),当然今天的主角并不是它。?

其实接触过后端开发的同学对于ssh应该都不陌生,可能每天你都在使用它,没错,当你要远程登录服务器的时候,大多数情况下都离不开它,俨然已经成为Linux系统的标准配置。所以,如果你使用的是Linux操作系统,那么默认情况下就已经自带ssh的客户端了,于是乎你直接可以在Linux的shell中执行: ssh user@host 就可以安全的登录到了远程主机host。对于ssh的更多命令或者玩法今天就不多介绍了,因为这不是今天的主要目标,今天的主要任务是实现一个和Linux操作系统中默认自带的ssh命令行客户端一样的使用go语言开发的ssh命令行客户端,当然由于时间篇幅有限,这次并不会实现原生ssh命令行客户端的全部功能,主要是能够实现远程登录到远程host,并能进行命令行操作。对于其他高级命令,如端口转发等将在后续完成。

工欲善其事必先利其器?

既然说了要快速打造,那么必然需要借助一些现有的工具包了,这边为了完成这个客户端,笔者对原生的go语言的ssh包进行了一下封装做了一个小工具包gosshtool,可以从码云或者github找到。有了它,再来做ssh的客户端就轻松多了。

开始设计?

首先,要完成一个命令行的ssh客户端,我们先来看下Linux下自带的ssh客户端是怎么工作的。这里所说的怎么工作,会站在比较高层的角度,因为ssh的整个通讯协议比较复杂,这里不过多介绍,原因是go提供的ssh包已经把底层的一些协议实现了,这里没必要自己再写一套实现出来,如果你确实对底层协议有兴趣,可以自己去网上查阅文档。那么站在比较高层角度来看,是如何的呢?

我们还原一个最常见的场景:某一天,你想登录远程主机,于是你打开了Linux的shell, 输入

ssh user@host

然后输入密码后顺利的登录了host这个主机,接着你在shell输入一些命令,比如

ls

查看远程主机当前目录下所有文件。

上述场景的过程,我们可以简单画一个图,来看看你这些操作是怎么与远程主机通讯的,如下图:

根据上图,我们开始设计,首先要想办法读取用户的键盘输入,如:输入pwd 在go语言中,我们可以使用os和bufio两个包,关键代码如下:

inputReader := bufio.NewReader(os.Stdin)
input, err := inputReader.ReadString('\n')

如上代码,我们就可以读取以换行结束的字符串。

这样完成了图中的第一步,第二步,我们将要建立与远程主机的ssh连接,这时候可以用到前面介绍的工具gosshtool了,有了它完成这一步变得轻松许多。在介绍这一步之前,我们先来对这个将要实现的客户端再多啰嗦几句,为了使我们的客户端看起来更像Linux自带的ssh客户端,我们假设将要做的这个客户端名字叫sshcmd,我们将要完成的任务是到时候生成一个叫sshcmd的可执行文件,然后执行

./sshcmd user@host

就建立了远程ssh连接,并返回远程主机登录信息,接着你可以继续在控制台输入后续命令,这些命令实际上是在远程主机执行的,就像Linux自带的ssh客户端一样。所以,我们还要用到go的一个叫做flag的包,这个包在写命令行程序的时候非常有用,它可以方便的对命令参数进行解析。所以我们会写到如下关键代码:

func main() {flag.StringVar(&host, "h", "", "host")flag.StringVar(&passwd, "p", "", "password")flag.Parse()hostsp := strings.Split(host, "@")user = hostsp[0]host = hostsp[1]
}

我们从命令行读取了user,host,password三个重要参数。有了它们,可以就可以建立ssh连接了关键代码如下:

 config := &gosshtool.SSHClientConfig{User:     user,Password: passwd,Host:     host,}sshclient := gosshtool.NewSSHClient(config)_, err := sshclient.Connect()if err == nil {fmt.Println("ssh connect success")} else {fmt.Println("ssh connect failed")}modes := ssh.TerminalModes{ssh.ECHO:          0,ssh.TTY_OP_ISPEED: 14400,ssh.TTY_OP_OSPEED: 14400,}pty := &gosshtool.PtyInfo{Term:  "xterm-256color",H:     80,W:     40,Modes: modes,}session, err := sshclient.Pipe(conn, pty, nil, 30)if err != nil {fmt.Println(err)}defer session.Close()

我们使用了gosshtool的NewSSHClient方法创建了一个客户端,并调用Connect()建立了连接,最后使用了Pipe(conn, pty, nil, 30)方法创建了一个保持会话,这样就号好了。这一切看起来如此简单,都要归功于Pipe这个方法,它的第一个参数是一个ReadWriteCloser接口类型,只要实现了该接口的结构都可以传入,这里我们会使用TCPConn这个结构,该结构实现了net.Conn接口,而net.Conn接口也是实现了ReadWriteCloser接口的。这个参数非常重要,我们建立了连接后,后续的通信全靠它了。你如果熟悉ReadWriteCloser接口,其实你就知道这个接口又组合了三个接口:

type ReadWriteCloser interface {ReaderWriterCloser
}type Writer interface {Write(p []byte) (n int, err error)
}type Reader interface {Read(p []byte) (n int, err error)
}type Closer interface {Close() error
}

再看net.Conn接口:

type Conn interface {// Read从连接中读取数据// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真Read(b []byte) (n int, err error)// Write从连接中写入数据// Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真Write(b []byte) (n int, err error)// Close方法关闭该连接// 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误Close() error// 返回本地网络地址LocalAddr() Addr// 返回远端网络地址RemoteAddr() Addr// 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline// deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞// deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作// 参数t为零值表示不设置期限SetDeadline(t time.Time) error// 设定该连接的读操作deadline,参数t为零值表示不设置期限SetReadDeadline(t time.Time) error// 设定该连接的写操作deadline,参数t为零值表示不设置期限// 即使写入超时,返回值n也可能>0,说明成功写入了部分数据SetWriteDeadline(t time.Time) error
}

对比下会发现Conn接口也实现了

  • Read(b []byte) (n int, err error)
  • Write(b []byte) (n int, err error)
  • Close() error

这也说明了,确实我们可以将net.Conn的参数传入。通过接口方法,其实也可以看出这些接口都有一个共同作用,可以对字节进行读写操作。而我们要与远程主机网络通信,当然少不了这些。因此,所有实现以上三个方法的结构都是可以传入并于远程主机建立的ssh连接通信的。这里,我们的想法是:在本地起一个socket服务,并接受标准输入,最终将标准输入的数据通过Pipe转发给远程主机,实现本地终端输入命令通过ssh协议远程执行如下图:

正如图中所示,实际上Pipe方法可以理解为将tcp连接转成了ssh连接并可以通过它传递数据。当然也可以将websocket的连接转成ssh连接,这样就可以实现基于web网页的ssh客户端了,也是非常简单的,这个后续介绍。介绍到这里,大部分关键的点都已经说完了,这里只是简单实现了一个最简单版本的ssh命令行客户端,当然通过gosshtool还可以做很多好玩的东西,比如部署工具,本地转发服务,命令行运维工具等。最后,最最关键的,放上本次实践的完整源码: sshcmd源码

总结

本文介绍了如何打造一个本地命令行ssh客户端,如果基于现成的工具包确实没多少工作量,而且大部分功能都实现比较粗糙,权当抛砖引玉。

转载于:https://my.oschina.net/u/1446855/blog/698722

使用go的ssh包快速打造一个本地命令行ssh客户端相关推荐

  1. 打造一个全命令行的Android构建系统

    IDE都是给小白程序员的,大牛级别的程序员一定是命令行控,终端控,你看大牛都是使用vim,emacs 就一切搞定" 这话说的虽然有些绝对,但是也不无道理,做开发这行要想效率高,自动化还真是缺 ...

  2. 如何提高百度指数,快速打造一个高权重网站

    最近在看一小说,小说名忘了. 讲的是修道.玄幻的,内容略有不同,不是纯粹的爽文类型,条条框框的礼节道义颇多.给我的第一感觉就是,这TM也能上排名榜? 小说这一领域,十有九九都是小白文.爽文的. 追求爽 ...

  3. 零基础快速打造一个属于自己的微信聊天工具

    " 零基础快速打造一个属于自己的微信聊天工具" 打开微信,我们可以和别人进行聊天,发送消息.非常方便,那微信是怎么来的呢​?这个本质的问题让人突发奇想,我们能不能做一个属于自己的微 ...

  4. linux 快速 命令快捷键,快速操作Linux终端命令行的快捷键列表

    快速操作Linux终端命令行的快捷键列表 在shell命令终端中,Ctrl+n相当于方向向下的方向键,Ctrl+p相当于方向向上的方向键. 在命令终端中通过它们或者方向键可以实现对历史命令的快速查找. ...

  5. 自己写的python软件可以在哪发布-如何发布一个Python命令行工具

    本文简介 上次写的一个终端里面斗鱼TV弹幕Python版本和Ruby版本,并且发布到PIP和RubyGems上面.在发布PIP包的时候,居然Google不到一篇可以非常好的讲解这个流程的文章.于是整理 ...

  6. 中断linux命令快捷键_实用!快速操作Linux终端命令行的快捷键

    原标题:实用!快速操作Linux终端命令行的快捷键 在shell命令终端中,Ctrl+n相当于方向向下的方向键,Ctrl+p相当于方向向上的方向键. 在命令终端中通过它们或者方向键可以实现对历史命令的 ...

  7. Homebrew进阶使用教程(二)-用一个命令行天气客户端构建自己的仓库

    [homebrew 系列文章] HomeBrew常规使用教程 Homebrew进阶使用教程(一) Homebrew进阶使用教程(二)-用一个命令行天气客户端构建自己的仓库 Homebrew进阶使用教程 ...

  8. python编写请求参数带文件_转载:如何编写一个带命令行参数的Python文件

    看到别人执行一个支持命令行参数的python文件,瞬间觉得高大上起来.牛逼起来,那么如何编写一个带命令行参数的python脚本呢?不用紧张,下面将简单易懂地让你学会如何让自己的python脚本,支持命 ...

  9. c语言编程实现二进制计算器,本程序是用纯C语言编的一个基于命令行的四则运算计算器。主要用于计算四则运算表达式的值,同时可以实现四...

    本程序是用纯C语言编的一个基于命令行的四则运算计算器.主要用于计算四则运算表达式的值,同时可以实现四 2016-08-22 0 0 0 暂无评分 其他 1 积分下载 如何获取积分? 本程序是用纯C语言 ...

最新文章

  1. NetworkX玩一下 --update@2017.06.28
  2. 奥数国家队最强6人集结,深圳中学独占2席,人大附中连续三年入围
  3. Linux如何访问mmio空间,一文读懂Linux下如何访问I/O端口和I/O内存
  4. 华为手机下拉菜单没了_用了三年才知道华为录屏这么强大!再不会用,手机钱打水漂了...
  5. 谷歌已推送 Android Q Beta 1
  6. Linux最常用的基础命令 下篇
  7. 利用拉普拉斯滤波器提取图像边缘,实现图像锐化
  8. JXTA第一步:HelloWorld
  9. stl格式导入matlab,机器人模型导入MATLAB(三):导入MATLAB URDF/stl 格式
  10. 《Java从入门到放弃》系列文章改版中...
  11. 用Python解读房贷利率,有没有套路?这是我见过最透彻的Python版解读!
  12. String对象的match方法
  13. python做正态分布的例子_python实现正态分布
  14. 高德地图JS--批量规划步行路线 优化
  15. 【小月电子】ALTERA FPGA开发板系统学习教程-LESSON5数码管动态显示
  16. 成本太高,京东配送扛不住了?
  17. JVM内存模型(一篇足以)
  18. 侏罗纪怪兽世界怎么登陆服务器未响应,全金属怪物一直登入不进去怎么办
  19. PaddleX快速实现图像分类训练
  20. 云服务器(一)基本操作

热门文章

  1. 哈希表和红黑树的对比
  2. 变参函数寻找最大整数值
  3. java 线程一起画图_java 多线程画图 不显示过程
  4. mysql 填充结果,mysql为测试数据库填充大量数据
  5. 记录一次服务器大中间表优化的问题(数据倾斜的解决)
  6. matlab 三维绘制
  7. ssm+maven+eclipse框架搭建
  8. 汇编语言:第三章 寄存器(内存访问)
  9. mvc源码解读(10)-ParameterDescriptor方法Action方法的参数描述对象
  10. 动网论坛新手详尽教程