0

This shouldn't be that complicated, but it seems that both the Ruby and Python Telnet libs have awkward APIs. Can anyone show me how to write a command to a Telnet host and then read the response into a string for some processing?

In my case "SEND" with a newline retrieves some temperature data on a device.

With Python I tried:

tn.write(b"SEND" + b"\r")
str = tn.read_eager()

which returns nothing.

In Ruby I tried:

tn.puts("SEND")

which should return something as well, the only thing I've gotten to work is:

tn.cmd("SEND") { |c| print c }

which you can't do much with c.

Am I missing something here? I was expecting something like the Socket library in Ruby with some code like:

s = TCPSocket.new 'localhost', 2000

while line = s.gets # Read lines from socket
  puts line         # and print them
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
bischoffingston
  • 637
  • 9
  • 27
  • 1
    While you can do very rudimentary handshake via Telnet, you really need to use an "expect"-like library to provide you with the ability to react to unexpected responses and/or long delays and timeouts. See http://stackoverflow.com/q/7142978/128421 for Ruby "expect" recommendations. – the Tin Man Jun 09 '15 at 23:23
  • So I looked at some of the expect libraries for python and ruby and it seems they have a similar setups as the telnet libs. is pexpect.expect() the same as telnetlib.wait_until() as far as my application goes? – bischoffingston Jun 10 '15 at 14:09
  • The other thing is I don't want to expect a response, I want to load the response into a variable. The expect method on these libs only seems to return an index if the match is found. – bischoffingston Jun 10 '15 at 14:16
  • Expect libraries allow you to capture results. They have to in order to do sub-string and regexp matches against the response of the host you're connected to, which then allows them to match CLI prompts, etc. – the Tin Man Jun 10 '15 at 16:50

1 Answers1

0

I found out that if you don't supply a block to the cmd method, it will give you back the response (assuming the telnet is not asking you for anything else). You can send the commands all at once (but get all of the responses bundled together) or do multiple calls, but you would have to do nested block callbacks (I was not able to do it otherwise).

require 'net/telnet'

class Client
  # Fetch weather forecast for NYC.
  #
  # @return [String]
  def response
    fetch_all_in_one_response
    # fetch_multiple_responses
  ensure
    disconnect
  end

  private

  # Do all the commands at once and return everything on one go.
  #
  # @return [String]
  def fetch_all_in_one_response
    client.cmd("\nNYC\nX\n")
  end

  # Do multiple calls to retrieve the final forecast.
  #
  # @return [String]
  def fetch_multiple_responses
    client.cmd("\r") do
      client.cmd("NYC\r") do
        client.cmd("X\r") do |forecast|
          return forecast
        end
      end
    end
  end

  # Connect to remote server.
  #
  # @return [Net::Telnet]
  def client
    @client ||= Net::Telnet.new(
      'Host'       => 'rainmaker.wunderground.com',
      'Timeout'    => false,
      'Output_log' => File.open('output.log', 'w')
    )
  end

  # Close connection to the remote server.
  def disconnect
    client.close
  end
end

forecast = Client.new.response
puts forecast
Ollie
  • 344
  • 1
  • 7