技术分享
数据库连接池
Vue双向数据绑定
访问授权协议-OAuth2
Golang基础赋能
Golang 基础赋能(一)
Golang基础赋能(二)
Golang基础赋能(三)
Golang基础赋能(四)
基于Web的Linux远程终端-Ansi转义序列
-
+
首页
基于Web的Linux远程终端-Ansi转义序列
### 问题背景 在agent端的开发中,我们实现了从Web端到Linux端的日志文件上传功能。但是随之我们便发现,想要将这个对应的日志记录下来并不是一件简单的事情,日志回放与日志下载在需求中存在矛盾。首先agent的架构并不是简单的A到B这么简单,实际我们在客户与linux服务器之间架起的这座桥梁,本质上竟由4部分组成! #### Agent架构组成 - agent前端,他就是一个单页面的终端,利用xterm的第三方框架构成,负责接收用户信息的传入,与agent端中的webtty使用websocket进行交互 - webtty,他负责接收xterm传入的信息,并与真正的linux客户端进行交互,负责将xterm传入的信息转接给我们得linux客户端 - sshproxy,他作为真正的linux端的前哨站,其实他才是真正的linux端的agent,他除了接收webtty传入的信息,也会接收像我们平时的第三方连接工具的连接,像xshell,finalshell等工具,都需要先连接他,然后在他这里,他会对命令进行解析、拦截,这里才是实现整个linux agent端核心功能的地方 - linux服务端,他是目标客户真正想连接的机器,负责将传入的命令解析并完成,并返回结果 ![](https://static.cloudcare.cn/cloudcare-mrdoc/img/2025-01-24_151618_9859000.6553742318913763.png) ### 解决方案步骤 #### 1.初步调查 我们查阅了之前的代码以及对应的日志信息,发现之前存放的日志文件中绝大部分都会带有一定的特殊字符。然而在web端的终端上,他却可以正常的显示。那么这些字符究竟是什么,他来自哪里,对终端中正常文字的展示造成了什么影响是我们当务之急最需要解决的问题 #### 2.验证字符的重要性 由步骤1中我们得到了3个问题,那么接下来我们最需要做的就是判断该字符对终端的影响。为了验证这个影响,我们删除了部分存放在oss中的字符,结果很明显,字符被删除之后,终端中的内容已然无法正常显示,这证明字符就是我们解决问题的关键。 #### 3.查找字符的来源 经历了步骤二之后,我们已然得出一个结论,问题的关键在于解决字符的含义,那么我们首要的步骤就是找到字符的来源,只有知道他的来源,我们才能更好的针对他解出密码的含义是什么。我们详细的分析了agent端的结构情况,由agent端架构我们可以得知,既然sshproxy负责代码内容的解析,那么必然内容的上传也在他那才是最正常的。果然我们在他这发现了往文件内内容写入的代码。并发现其实内容是来源于linux服务端。 #### 4.解析字符 字符的来源知道后我们通过查阅了大量资料发现,linux端控制终端显示的内容被称之为ANSI转义序列,他对用户是不可见的,但是确控制了linux端的各种内容展示,像是颜色、光标上移、清除内容等都是由他控制的。然后我们查阅了我们所需要的转义序列。只剩下最后一步,那就是将日志传入到sshproxy即可 #### 5.如何解决日志传入的问题 webtty由于原先并没有设计要传入额外内容的功能,因为之前的终端并不会提供额外的上传功能,只需要将命令传递即可,他之前于sshproxy交互是通过将内容写入一个文件中,然后sshproxy则是定时读取这个文件去进行解析实现的。因此传递信息也成为一个问题。不过我们通过在日志信息前标记特殊前缀去解决了这个问题。 #### 6.解决问题 解决上述问题之后,我们只需要做特殊字符拦截,让这些字符不进入linux服务端,影响代码的输出结果。为了防止日志影响正常的命令展示,我们又采用了将日志输出到终端最下面的方式,下面是我们将日志记录到页面最底下的代码,代码如下 ```go logRecordPrefix := "log_record:" if idx := strings.Index(data, logRecordPrefix); idx != -1 { buf = buf[idx+len(logRecordPrefix):] // 创建 ANSI 转义序列 saveCursor := []byte("\x1b[0G\x1b[999B") // 保存字符及属性 //clearScreen := []byte("\x1b[H") // 清屏并移动光标到左上角 restoreCursor := []byte("\x1B[0G") // 恢复字符及属性 // 添加保存光标、清屏、日志和恢复光标命令到切片 buf = append(append(append(saveCursor, saveCursor...), buf...), restoreCursor...) // 打印结果,验证换行符是否添加 //fmt.Println(string(buf)) } ``` ### 总结 此问题的解决不仅优化了日志记录,并深刻理解了之前的代码结构,还深入揭示了 ANSI 转义序列的终端显示控制原理,为后续开发和问题排查积累了宝贵经验。
吴晓俊
2025年1月24日 15:26
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码