client.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "os/signal"
  10. "strings"
  11. "sync/atomic"
  12. "syscall"
  13. "time"
  14. "github.com/google/uuid"
  15. "hnyfkj.com.cn/rtu/linux/baseapp"
  16. )
  17. const MODULE_NAME = "YFKJ_SSH_CLIENT"
  18. var (
  19. coupler *MQTTCoupler
  20. Version = "1.0.0.1"
  21. ErrBrokerAddressEmpty = errors.New("mqtt server address is empty")
  22. ErrIMEINotAvailable = errors.New("device imei is not available")
  23. )
  24. func main() {
  25. if baseapp.IsArgsParam("-h") {
  26. help()
  27. return
  28. }
  29. if baseapp.IsArgsParam("-v") {
  30. fmt.Println("程序版本:", Version, "\n构建时间:", baseapp.BuildTime)
  31. return
  32. }
  33. devIMEI := baseapp.GetArgsParamStr("-c", "")
  34. if devIMEI == "" {
  35. help()
  36. return
  37. }
  38. if err := loadAppConfig(); err != nil {
  39. fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, err)
  40. return
  41. }
  42. if CfgServers.MQTTSrv.Address == "" {
  43. fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, ErrBrokerAddressEmpty)
  44. return
  45. }
  46. ctx, cancel := context.WithCancel(context.Background())
  47. coupler = &MQTTCoupler{
  48. ctx: ctx,
  49. cancel: cancel,
  50. broker: CfgServers.MQTTSrv.Address,
  51. username: CfgServers.MQTTSrv.Username,
  52. password: CfgServers.MQTTSrv.Password,
  53. clientID: uuid.New().String(),
  54. imei: devIMEI,
  55. cwd: "/",
  56. }
  57. if err := coupler.init2(); err != nil {
  58. fmt.Printf("[%s] 错误: %v\n!!", MODULE_NAME, err)
  59. return
  60. }
  61. term() // 启动终端模拟器卍
  62. }
  63. // SHELL终端模拟器
  64. // 1, 连接远程设备, 是否成功
  65. // 2, 等待用户输入, 封装请求
  66. // 3, 没有用户输入, 封装心跳
  67. // 4, 发送请求数据, 远程执行
  68. // 5, 耗时用户请求, 允许中断, Ctrl+C
  69. // 6, 等待返回结果, 打印输出
  70. // 7, 循环等待下次, 直到退出
  71. func term() {
  72. var pingState atomic.Bool
  73. var executing atomic.Bool
  74. // -在线-心跳检测
  75. heartbeatLoop(&pingState)
  76. // Ctrl+C中断处理
  77. interruptLoop(&executing)
  78. reader := bufio.NewReader(os.Stdin)
  79. for {
  80. if !pingState.Load() {
  81. fmt.Printf("[%s] 无法连接目标设备!!\n", MODULE_NAME)
  82. break
  83. }
  84. fmt.Print("\033[?25h") // 显示光标
  85. fmt.Printf("root@%s:%s# ", coupler.imei, coupler.cwd)
  86. input, err := reader.ReadString('\n')
  87. if err != nil {
  88. if err == io.EOF {
  89. os.Exit(0)
  90. }
  91. fmt.Println("读取用户输入失败:", err)
  92. continue
  93. }
  94. input = strings.TrimSpace(input)
  95. if input == "" {
  96. continue
  97. }
  98. switch input {
  99. case "quit":
  100. _, _ = coupler.quit()
  101. os.Exit(0)
  102. }
  103. fmt.Print("\033[?25l") // 隐藏光标
  104. executing.Store(true)
  105. result, err := coupler.exec(input)
  106. executing.Store(false)
  107. if err != nil {
  108. fmt.Printf("执行错误: %v\n", err)
  109. continue
  110. }
  111. if result.Stdout != "" {
  112. fmt.Println(result.Stdout)
  113. }
  114. if result.Stderr != "" {
  115. fmt.Fprintln(os.Stderr, result.Stderr)
  116. }
  117. if result.Cwd != "" {
  118. coupler.cwd = result.Cwd
  119. }
  120. }
  121. }
  122. func help() {
  123. h := `
  124. -h 显示帮助提示
  125. -v 当前程序版本
  126. -c 连接目标设备(IMEI), 例如: -c 869523059113051
  127. `
  128. fmt.Println(h)
  129. }
  130. func interruptLoop(executing *atomic.Bool) {
  131. sigCh := make(chan os.Signal, 1)
  132. signal.Notify(sigCh, syscall.SIGINT)
  133. go func() {
  134. for range sigCh {
  135. if executing.Load() {
  136. _, _ = coupler.stop()
  137. }
  138. fmt.Print("\n^C\n")
  139. } // end for
  140. }()
  141. }
  142. func heartbeatLoop(pingState *atomic.Bool) {
  143. go func() {
  144. ticker := time.NewTicker(1 * time.Second)
  145. defer ticker.Stop()
  146. for range ticker.C {
  147. resp, err := coupler.ping()
  148. if err == nil && resp.Error == nil && resp.Result != nil && string(resp.Result) == "pong" {
  149. pingState.Store(true)
  150. } else {
  151. pingState.Store(false)
  152. } // end if
  153. } // end for
  154. }()
  155. }