通过Socket连接发送GUI/TUI

5
最近我一直在尝试使用golang创建一个程序,它运行在服务器上并接受telnet连接。然后,我想打开一个TUI(文本用户界面),比如curses菜单(在golang的情况下,类似于:termuigocui等)在telnet连接上。我的问题是,我该如何做到这一点,或者是否可能?我已经尝试过在接受连接时启动TUI,但它只会在服务器端打开,而不是在telnet客户端端打开。据我所知,没有简单的方法可以通过telnet或任何其他socket IO连接发送TUI。感谢任何帮助来解决这个问题。谢谢!:D

至少,将进程的stdin、stdout和stderr重新分配到套接字FD上可能对某些程序有效。但是,根据TUI的性质,您可能需要创建一个伪终端,在终端控制器和套接字之间转发数据,解码通过telnet协议接收到的终端大小更改事件并将其转发给终端控制器等。 - Daniel Schepler
对于我所考虑的应用程序,我并不特别关注终端大小的变化。我想我可能需要解释Telnet字节码,虽然我可以做到,但这可能会很麻烦。也许这是一个我应该开发的功能,但可能不是立即需要的。然而,为数据转发创建伪终端会导致某些终端仿真存在兼容性问题,不是吗? - jsmith
1个回答

5
首先,你应该注意到我提供的示例是完全不安全的(不要在互联网上公开它!),而且也没有考虑到诸如信号处理或终端大小调整之类的问题(你可能想考虑使用SSH代替)。
但是为了回答您的问题,这里有一个运行TCP服务器并连接远程客户端到运行在本地PTY中的termui程序的示例(使用https://github.com/gizak/termuihttps://github.com/kr/pty软件包)。
package main

import (
    "flag"
    "io"
    "log"
    "net"
    "os"
    "os/exec"

    ui "github.com/gizak/termui"
    "github.com/kr/pty"
)

var termuiFlag = flag.Bool("termui", false, "run a termui example")

func main() {
    flag.Parse()

    var err error
    if *termuiFlag {
        err = runTermui()
    } else {
        err = runServer()
    }
    if err != nil {
        log.Fatal(err)
    }
}

// runTermui runs the termui "Hello World" example.
func runTermui() error {
    if err := ui.Init(); err != nil {
        return err
    }
    defer ui.Close()

    p := ui.NewParagraph("Hello World!")
    p.Width = 25
    p.Height = 5
    ui.Render(p)

    for e := range ui.PollEvents() {
        if e.Type == ui.KeyboardEvent {
            break
        }
    }

    return nil
}

// runServer listens for TCP connections on a random port and connects
// remote clients to a local PTY running the termui example.
func runServer() error {
    ln, err := net.Listen("tcp", "127.0.0.1:0")
    if err != nil {
        return err
    }
    defer ln.Close()
    log.Printf("Listening for requests on %v", ln.Addr())
    for {
        conn, err := ln.Accept()
        if err != nil {
            return err
        }
        log.Printf("Connecting remote client %v to termui", conn.RemoteAddr())
        go connectTermui(conn)
    }
}

// connectTermui connects a client connection to a termui process running in a
// PTY.
func connectTermui(conn net.Conn) {
    defer func() {
        log.Printf("Closing remote client %v", conn.RemoteAddr())
        conn.Close()
    }()

    t, err := pty.StartWithSize(
        exec.Command(os.Args[0], "--termui"),
        &pty.Winsize{Cols: 80, Rows: 24},
    )
    if err != nil {
        log.Printf("Error starting termui: %v", err)
        return
    }
    defer t.Close()

    go io.Copy(t, conn)
    io.Copy(conn, t)
}

示例用法是在一个窗口中运行此程序,并在另一个窗口中使用nc连接:

$ go run server.go
2019/01/18 01:39:37 Listening for requests on 127.0.0.1:56192

$ nc 127.0.0.1 56192

您应该看到“Hello world”框(按回车键断开连接)。

非常感谢!这个十分完美。我对 Go 中的网络编程还有些陌生,所以请原谅我的新手操作。这对你来说可能很简单。我只是在 C 网络编程中使用 ncurses 管道方面有些经验,相比之下,这有点过时了,而且通常使用像 socat 这样的工具来轻松地将连接管道化到应用程序中。不过这次我想把它打包到一个单一的应用程序中,因为我觉得这样更整洁,可以在 Heroku、AWS 等云平台上进行托管。所以再次感谢你! - jsmith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接