最近在用golang写交互shell的代码,因为涉及到三端的问题(浏览器、服务端、agent)需要一个websocket转发这部分。
问题就此开始
1、如何实现websocket转发,打通浏览器和agent的通讯?
这是基本架构,是不是挺简单的
浏览器->服务端->agent
agent->服务端->浏览器
2、如何把浏览器和agent对应起来,不让乱了套?
那就撸起袖子加油干,一段代码一包烟,一个破功能搞几天,我的基本思路:
浏览器带有唯一标识websocket连接到服务端的handler1,把conn、唯一标识一起存到全局中(handler1只接收浏览器的连接)。
agent带有唯一标识websocket连接到服务端的handler2,通过判断唯一标识打通浏览器和agent。
这里就不贴出浏览器和agent的代码了。
使用到的项目一并分享(你看是不是很体贴):
linux交互shell:
https://github.com/creack/pty
windows交互shell(可能低版本windows不兼容):
https://github.com/ActiveState/termtest/conpty
前端xterm:
https://github.com/xtermjs/xterm.js
划重点:要想打通浏览器和agent的通讯,服务端作为中间人那么这时就需要下面服务端的相关操作了,不知道在哪个issues看到的忘记了,顺便记录一下,直接看服务端代码,非常优雅:
serverClosed := make(chan struct{})
clientClosed := make(chan struct{})
go copyConn(client.ws, ws, serverClosed, clientClosed)
go copyConn(ws, client.ws, clientClosed, serverClosed)
func copyConn(readConn, writeConn *websocket.Conn, readClosed, writeClosed chan struct{}) {
var rerr error
for {
var r io.Reader
var messageType int
messageType, r, rerr = readConn.NextReader()
if rerr != nil {
break
}
w, err := writeConn.NextWriter(messageType)
if err != nil {
break
}
if _, err := io.Copy(w, r); err != nil {
break
}
if err := w.Close(); err != nil {
break
}
}
// Close the reading connection. If we broke out of the loop because of a
// normal close, then NextReader echoed the close message and we should now
// close the connection. If it's an abnormal close, then we should give up
// and close the connection.
readConn.Close()
// Tell the other goroutine that readConn was closed.
close(readClosed)
// Did we break out of the loop because we received a close message?
if e, ok := rerr.(*websocket.CloseError); ok && e.Code != websocket.CloseAbnormalClosure {
// Forward the close message to writeConn.
var m []byte
if e.Code != websocket.CloseNoStatusReceived {
m = websocket.FormatCloseMessage(e.Code, e.Text)
}
err := writeConn.WriteMessage(websocket.CloseMessage, m)
// Did we successfully send the close message?
if err == nil {
// Wait with timeout for the other goroutine to handle the handshake.
select {
case <-writeClosed:
// The other goroutine closed writeConn.
return
case <-time.After(10 * time.Second):
}
}
}
// A blocked reader returns with an error when the connection is closed.
writeConn.Close()
}
仅限交流学习使用,如您在使用本工具或代码的过程中存在任何非法行为,您需自行承担相应后果,我们将不承担任何法律及连带责任。“如侵权请私聊公众号删文”。
原文始发于微信公众号(柠檬赏金猎人):golang实现的交互shell
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论