Skip to content

Commit bb23fdb

Browse files
author
yckj0834
committed
Sun Mar 8 03:09:59 CST 2020
1 parent 6ec68fe commit bb23fdb

File tree

1 file changed

+0
-218
lines changed

1 file changed

+0
-218
lines changed

README.md

Lines changed: 0 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -129,224 +129,6 @@ this.ws.onclose = () => {
129129
* [Xterm.js v3](https://github.com/lflxp/gin-xterm/blob/master/src/views/pty/xterm3.vue)
130130
* [Xterm.js v4](https://github.com/lflxp/gin-xterm/blob/master/src/views/pty/index.vue)
131131

132-
## Golang Websocket
133-
134-
1. 不需要解析Quit操作,那怎么判断程序结束去杀掉线程呢
135-
136-
解答:根据`exec.Command``cmd.Wait()`去判断,退出自动触发`quitChan <- true`去结束线程
137-
138-
2. 后端websocket使用的什么项目
139-
140-
解答: `github.com/gorilla/websocket`
141-
142-
3. 如何实现golang命令行exec.Command的结果实时联动操作的?
143-
144-
解答:通过pty/tty在linux启动一个pid线程实现的,具体资料如下:
145-
146-
* [Linux中tty、pty、pts的概念区别](https://blog.csdn.net/fuhanghang/article/details/83691158)
147-
* [golang pty pkg](github.com/creack/pty)
148-
149-
4. gin或者go http如何做到websocket连接的切换?
150-
151-
解答:`http`升级为[websocket](github.com/gorilla/websocket)
152-
153-
```golang
154-
...
155-
156-
import "github.com/gorilla/websocket"
157-
158-
var upGrader = websocket.Upgrader{
159-
ReadBufferSize: 1024 * 1024,
160-
WriteBufferSize: 1024 * 1024 * 10,
161-
CheckOrigin: func(r *http.Request) bool {
162-
return true
163-
},
164-
}
165-
166-
func ping(c *gin.Context) {
167-
//升级get请求为webSocket协议
168-
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
169-
if err != nil {
170-
return
171-
}
172-
defer ws.Close()
173-
174-
// todo
175-
...
176-
}
177-
```
178-
179-
5. 如何处理每个websocket的多个goroutine正常退出
180-
181-
解答:通过quit Channel + for select
182-
183-
```golang
184-
// 发送命令的执行结果
185-
// 不执行具体任务
186-
func (this *ClientContext) Send(quitChan chan bool) {
187-
defer setQuit(quitChan)
188-
189-
buf := make([]byte, 1024)
190-
191-
for {
192-
select {
193-
case <-quitChan:
194-
log.Info("Close Send Channel")
195-
return
196-
default:
197-
// 读取命令执行结果并通过ws反馈给用户
198-
size, err := this.Pty.Read(buf)
199-
if err != nil {
200-
log.Errorf("%s命令执行错误退出: %s", this.Request.RemoteAddr, err.Error())
201-
return
202-
}
203-
log.Infof("Send Size: %d buf: %s buf[:size]: %s\n", size, string(buf), string(buf[:size]))
204-
if err = this.write(buf[:size]); err != nil {
205-
log.Error(err.Error())
206-
return
207-
}
208-
}
209-
}
210-
}
211-
```
212-
213-
6. 如何判断各种操作类型以及怎么执行
214-
215-
解答:
216-
217-
* 预先设计数据格式
218-
* for select + switch
219-
220-
```golang
221-
// 判断命令
222-
// @Params msg:message
223-
switch message[0] {
224-
case Input:
225-
// TODO: 用户是否能写入
226-
if !this.Xtermjs.Options.PermitWrite {
227-
break
228-
}
229-
230-
// base64解码
231-
decode, err := utils.DecodeBase64Bytes(string(message[1:]))
232-
if err != nil {
233-
log.Error(err.Error())
234-
break
235-
}
236-
237-
// 向pty中传入执行命令
238-
_, err = this.Pty.Write(decode)
239-
if err != nil {
240-
log.Error(err.Error())
241-
return
242-
}
243-
case Ping:
244-
this.write([]byte("pong"))
245-
case ResizeTerminal:
246-
// @数据格式 type+rows:cols
247-
// base64解码
248-
decode, err := utils.DecodeBase64(string(message[1:]))
249-
if err != nil {
250-
log.Error(err.Error())
251-
break
252-
}
253-
254-
tmp := strings.Split(decode, ":")
255-
rows, err := strconv.Atoi(tmp[0])
256-
if err != nil {
257-
log.Error(err.Error())
258-
this.write([]byte(err.Error()))
259-
break
260-
}
261-
cols, err := strconv.Atoi(tmp[1])
262-
if err != nil {
263-
log.Error(err.Error())
264-
this.write([]byte(err.Error()))
265-
break
266-
}
267-
window := struct {
268-
row uint16
269-
col uint16
270-
x uint16
271-
y uint16
272-
}{
273-
uint16(rows),
274-
uint16(cols),
275-
0,
276-
0,
277-
}
278-
syscall.Syscall(
279-
syscall.SYS_IOCTL,
280-
this.Pty.Fd(),
281-
syscall.TIOCSWINSZ,
282-
uintptr(unsafe.Pointer(&window)),
283-
)
284-
default:
285-
this.write([]byte(fmt.Sprintf("Unknow Message Type %s", string(message[0]))))
286-
log.Error("Unknow Message Type %v", message[0])
287-
}
288-
```
289-
290-
7. 如何设置exec.Command在pty终端的窗口大小
291-
292-
解答:
293-
294-
```golang
295-
window := struct {
296-
row uint16
297-
col uint16
298-
x uint16
299-
y uint16
300-
}{
301-
uint16(rows),
302-
uint16(cols),
303-
0,
304-
0,
305-
}
306-
syscall.Syscall(
307-
syscall.SYS_IOCTL,
308-
this.Pty.Fd(),
309-
syscall.TIOCSWINSZ,
310-
uintptr(unsafe.Pointer(&window)),
311-
)
312-
```
313-
314-
8. 还需要注意什么
315-
316-
* 明确命令下发渠道和作用
317-
318-
> this.Pty.Write([]byte(***))
319-
320-
* 明确websocket下发渠道和作用
321-
322-
> this.WsConn.Write([]byte(***))
323-
324-
* 如何操作正在执行中的cmd程序
325-
326-
> this.Cmd.Process.Signal(syscall.Signal(this.Xtermjs.Options.CloseSignal))
327-
328-
* 明确一个exec.Command和一个webscoket.Conn如何对一个http请求保持长连接请求的
329-
330-
一个http.Request请求由cmd.Wait() + go Send() + go Receive() + Channel 将一个完整链路进行串联起来
331-
332-
* 如何下手?设计一个符合场景的struct
333-
334-
```golang
335-
// 服务端内部处理对象
336-
type ClientContext struct {
337-
Xtermjs *XtermJs // 前端配置
338-
Request *http.Request // http客户端请求
339-
WsConn *websocket.Conn // websocket连接
340-
Cmd *exec.Cmd // exec.Command实例
341-
Pty *os.File // 命令行pty代理
342-
Cache *bytes.Buffer // 命令缓存
343-
CacheMutex *sync.Mutex // 缓存并发锁
344-
WriteMutex *sync.Mutex // 并发安全 通过ws发送给客户
345-
}
346-
```
347-
348132
## 参考
349133

350-
* https://github.com/yudai/gotty @release-1.0
351-
* https://github.com/creack/pty
352134
* https://mojotv.cn/2019/05/27/xtermjs-go

0 commit comments

Comments
 (0)