本文介绍: websocket主要是ws或wss协议,其原理就是http协议升级成ws协议,即ws是建立在http上的,所有路由正常写http的路由,然后处理一下websocket升级。在最近一次需求里,需要实现一个webSSH的功能,就是把terminal搬到web中来。此方法需要输入,输出,和错误,使用标准的输入及标准输出,能实现交互,但是我是需要接收websocket发的消息,及返回websocket输出。注: 实现read方法时,注意加个回车,不然指令是不会执行的,我在这里就卡了很久……
在最近一次需求里,需要实现一个webSSH的功能,就是把terminal搬到web中来。要实现这个功能,可以采用websocket+ssh来说实现
websocket主要是ws或wss协议,其原理就是http协议升级成ws协议,即ws是建立在http上的,所有路由正常写http的路由,然后处理一下websocket升级。
路由:
backendApi.GET("/tools/ssh/ws", tools.WebSSH).Name = "webssh"
func WebSSH(e echo.Context) error {
var param tools.WebSShReq
if err := utils.BindAndValidate(e, &param); err != nil {
return err
}
if err := tools.Upgrade(e.Response().Writer, e.Request(), param); err != nil {
return err
}
return e.JSON(http.StatusOK, "'")
}
:
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func Upgrade(w http.ResponseWriter, r *http.Request, param WebSShReq) (err error) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
client := NewSSHClient(param)
client.Ws = conn
err = client.GenerateClient()
if err != nil {
fmt.Println("链接ssh错误", err)
conn.WriteMessage(1, []byte(err.Error()))
conn.Close()
return err
}
go client.Write()
return nil
}
type SSHClient struct {
Username string `json:"username"`
Password string `json:"password"`
IpAddress string `json:"ipaddress"`
Port int `json:"port"`
Client *ssh.Client
Ws *websocket.Conn
Session *ssh.Session
}
// NewSSHClient 创建新的ssh客户端时
func NewSSHClient(param WebSShReq) SSHClient {
client := SSHClient{}
client.Username = param.Username
client.Port = param.Port
client.IpAddress = param.IpAddress
client.Password = param.Password
return client
}
func (t *SSHClient) GenerateClient() error {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
config ssh.Config
err error
)
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(t.Password))
config = ssh.Config{
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
}
clientConfig = &ssh.ClientConfig{
User: t.Username,
Auth: auth,
Timeout: 5 * time.Second,
Config: config,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr = fmt.Sprintf("%s:%d", t.IpAddress, t.Port)
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return err
}
t.Client = client
return nil
}
func (t *SSHClient) send(out, stderr *bytes.Buffer, cmd []byte) error {
session, err := t.Client.NewSession()
if err != nil {
return errors.New("ssh session创建失败")
}
defer session.Close()
session.Stdout = out
session.Stderr = stderr
return session.Run(string(cmd))
}
func (t *SSHClient) Write() {
defer t.Client.Close()
for {
// p为用户输入
_, p, err := t.Ws.ReadMessage()
if err != nil {
return
}
fmt.Println("webssh:", string(p))
go func(data []byte) {
var (
out bytes.Buffer
stdEr bytes.Buffer
)
err2 := t.send(&out, &stdEr, data)
if err2 != nil {
t.Ws.WriteMessage(1, stdEr.Bytes())
return
}
t.Ws.WriteMessage(1, out.Bytes())
}(p)
}
}
3.改进
func (t *SSHClient) RunTerminal(stdout, stderr io.Writer, stdin io.Reader) error {
session, err := t.Client.NewSession()
if err != nil {
return err
}
defer session.Close()
session.Stdout = io.MultiWriter(os.Stdout, stdout)
session.Stderr = io.MultiWriter(os.Stderr, stderr)
session.Stdin = stdin
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
if err := session.RequestPty("xterm", 32, 160, modes); err != nil {
return err
}
if err = session.Shell(); err != nil {
log.Fatalf("start shell error: %s", err.Error())
}
if err = session.Wait(); err != nil {
log.Fatalf("return error: %s", err.Error())
}
return nil
}
此方法需要输入,输出,和错误,使用标准的输入及标准输出,能实现交互,但是我是需要接收websocket发的消息,及返回websocket输出。
type stdout struct {
ws *websocket.Conn
}
func (s *stdout) Write(p []byte) (n int, err error) {
// fmt.Println("SSH output:", string(p))
err = s.ws.WriteMessage(1, p)
return len(p), err
}
type stdIn struct {
ws *websocket.Conn
}
func (s *stdIn) Read(p []byte) (n int, err error) {
_, p2, err := s.ws.ReadMessage()
// 指令需要加一个回车
if err != nil {
return 0, err
}
n = copy(p, []byte(fmt.Sprintf("%sn", string(p2))))
// fmt.Println("####", string(p))
return n, err
}
func (s *stdIn) Close() error {
return nil
}
注: 实现read方法时,注意加个回车,不然指令是不会执行的,我在这里就卡了很久……
go client.Write()
换成
go client.RunTerminal(&stdout{conn}, &stdout{conn}, &stdIn{conn})
就ok了。
原文地址:https://blog.csdn.net/wangge20091126/article/details/130113262
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_45222.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。