go-telnet icon indicating copy to clipboard operation
go-telnet copied to clipboard

Client hangs for a minute at first.

Open smegeath opened this issue 3 years ago • 1 comments

My telnet client is connecting to a non-go-telnet server and uses the basic client code provided in the documentation example. Connecting to my server with a standard telnet client gets an instant reply, but for some reason, my go-telnet client hangs reading for exactly a minute, then is fully interactive. Is there some configuration I should be using or something extra I should be doing with the Conn to flush it?

smegeath avatar Feb 08 '21 05:02 smegeath

according to the documentation for io.Reader

If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.

This library does not follow that convention. It appears to block until it is able to fill the buffer.

This program demonstrates the issue
package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"time"

	"github.com/reiver/go-telnet"
)

// start a TCP server that
// - prints "hello world"
// - waits 60 seconds
// - then hangs up
func DemoServer() {
	listener, err := net.Listen("tcp", ":1111")
	if err != nil {
		panic(err)
	}
	defer listener.Close()

	for {
		conn, err := listener.Accept()
		if err != nil {
			panic(err)
		}
		conn.Write([]byte("hello world\n"))
		time.Sleep(10 * time.Second)
		conn.Close()
	}
}

// ConnectWithBufferSize connects to the server at the given address and
// and prints the output of each call to Read() using the given buffer size.
func ConnectWithBufferSize(bufferSize int) {
	telnetClient, err := telnet.DialTo("localhost:1111")
	if err != nil {
		panic(err)
	}
	defer telnetClient.Close()

	buff := make([]byte, bufferSize)
	for {
		n, err := telnetClient.Read(buff)
		log.Printf("EOF:%t> %#v\n", err == io.EOF, string(buff[:n]))
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
	}
}

func main() {
	go DemoServer()
	fmt.Println("=== Buffer Size 7 ===")
	ConnectWithBufferSize(7)
	fmt.Println("=== Buffer Size 1 ===")
	ConnectWithBufferSize(1)
}

Output:

=== Buffer Size 7 ===
2022/03/30 15:45:06 EOF:false> "hello w"
2022/03/30 15:45:16 EOF:true> "orld\n"
=== Buffer Size 1 ===
2022/03/30 15:45:16 EOF:false> "h"
2022/03/30 15:45:16 EOF:false> "e"
2022/03/30 15:45:16 EOF:false> "l"
2022/03/30 15:45:16 EOF:false> "l"
2022/03/30 15:45:16 EOF:false> "o"
2022/03/30 15:45:16 EOF:false> " "
2022/03/30 15:45:16 EOF:false> "w"
2022/03/30 15:45:16 EOF:false> "o"
2022/03/30 15:45:16 EOF:false> "r"
2022/03/30 15:45:16 EOF:false> "l"
2022/03/30 15:45:16 EOF:false> "d"
2022/03/30 15:45:16 EOF:false> "\n"
2022/03/30 15:45:26 EOF:true> ""

Note that with a buffer size of 7 you immediately get hello w then you have to wait the full 10 seconds to get orld\n. A quick test with a debugger shows that it is hanging here

As a work around, you can use a buffer size of 1

I am not able to re-produce the behavior OP describes where the connection ever becomes interactive.

I have confirmed that this is fixed by either of PR #22 or PR #9.

9072997 avatar Mar 30 '22 21:03 9072997