如何从Telnet会话中读取数据

3

我想创建一个工具,通过Telnet连接到网络设备并发送一些命令(类似于Expect,带有某些额外的要求),使用go-telnet

目前我已经成功建立了连接并发送了一些像这样的命令:

func main() {
    var loginBuffer = [6]byte{'r', 'o', 'o', 't', '\r', '\n'}
    var login = loginBuffer[:]

    conn, err := telnet.DialTo("10.10.10.2:23")
    if nil != err {
        fmt.Println(err)
    }
    defer conn.Close()

    conn.Write(login)
}

使用Wireshark我可以看到设备有响应,但我无法读取任何响应数据。猜测我可能在错误地使用Read(),但不确定。希望能提供一个可行的示例或者解释如何在这种情况下捕获和处理响应数据。
2个回答

4

感谢所有抽出时间回答我的问题的人。我成功地解决了问题:

  1. 每次创建读缓冲区时都太大(1024字节),因此程序一直等待它填满。现在我正在使用循环读取1个字节的缓冲区。

  2. 似乎,我还需要某些标准来判断函数何时停止读取并执行发送命令。

这里是可工作的代码片段:

// Thin function reads from Telnet session. "expect" is a string I use as signal to stop reading
func ReaderTelnet(conn *telnet.Conn, expect string) (out string) {
    var buffer [1]byte
    recvData := buffer[:]
    var n int
    var err error

    for {
        n, err = conn.Read(recvData)
        fmt.Println("Bytes: ", n, "Data: ", recvData, string(recvData))
        if n <= 0 || err != nil || strings.Contains(out, expect) {
            break
        } else {
            out += string(recvData)
        }
    }
    return out
}

//convert a command to bytes, and send to Telnet connection followed by '\r\n' 
func SenderTelnet(conn *telnet.Conn, command string) {
    var commandBuffer []byte
    for _, char := range command {
        commandBuffer = append(commandBuffer, byte(char))
    }

    var crlfBuffer [2]byte = [2]byte{'\r', '\n'}
    crlf := crlfBuffer[:]

    fmt.Println(commandBuffer)

    conn.Write(commandBuffer)
    conn.Write(crlf)
}

func main() {
    conn, err := telnet.DialTo("10.10.10.2:23")
    if nil != err {
        fmt.Println(err)
    }

    fmt.Print(ReaderTelnet(conn, "Login"))
    SenderTelnet(conn, "root")
    fmt.Print(ReaderTelnet(conn, "Password"))
    SenderTelnet(conn, "root")
    fmt.Print(ReaderTelnet(conn, ">"))
}

我现在想我明白了。好的,套接字上的读取会在读取操作系统维护的套接字缓冲区中可用的尽可能多的字节后返回。因此,读取长度为1024的切片应该是可以的——读取1到1024个字节。如果由Telnet包提供的连接工作方式不同,则是其中的一个错误。现在,据我所知,您的设备实现了文本协议。这些协议最好通过使用“逐行”处理来处理;在您的情况下,一行是由CRLF序列终止的任意数量的字节。 - kostix
为了处理这个问题,首先需要将您的Conn包装在一个bufio.Reader中,然后使用它的ReadLine或ReadBytes方法来读取完整的行。 - kostix
谢谢你的示例,Alexander。我有一个问题:我注意到如果(例如)“登录”字符串从未出现并且输入流结束,此语句将永远等待: n,err = conn.Read(recvData) 有人注意到这种行为吗? 我尝试使用time.AfterFunc函数进行操作,但没有成功。 - Cristian Veronesi
嗨,Cristian。我建议你检查一个不同的Telnet库:https://github.com/ziutek/telnet 它有一组示例,可以为您的需求进行微调,以实现类似expect的功能。至于终止读取过程,您应该在for循环中使用select和默认子句来检查超时到期。 - Alexander

0
你的读取操作从哪个连接中进行? 我认为你需要调用conn.read(buffer)从连接中读取数据,并将其写入缓冲区。
https://godoc.org/github.com/reiver/go-telnet#Conn.Read

这些示例对你正在使用的包没有太大帮助。也许下面这个来自不同 telnet go 包的示例会更有帮助。

https://github.com/ziutek/telnet/blob/master/examples/unix-cisco/main.go

这正是我所做的,然而执行在 conn.Read() 处“挂起”,好像期待接收到某些内容却从未接收到。说实话,我甚至无法想象该如何处理它=( - Alexander
3
@Alexander,那么为什么你决定不包含这段代码到你的问题中?你基本上是在问为什么某个东西不起作用,而没有展示那个“某个东西”的真正内容。请提供一个完整的不能运行的代码片段。最好能看到设备发送的数据(以便将其与你所做的操作进行对比)。 - kostix
@kostix 很抱歉,由于我尝试了大约十个不同的选项,但都无法使用,所以我不确定该发布哪一个。 - Alexander

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