Telnet usage

From FlightGear wiki
Jump to navigation Jump to search

FlightGear comes with an internal command server. This so called "telnet" server provides a "remote shell" (like a command prompt) into the running FlightGear process that can be used to inspect and modify runtime state, but also to run commands.

FlightGear can be configured to act like a telnet or even an http "server". It can watch a port for incoming connections and respond appropriately, these built-in daemons (servers) support multiple concurrent connections.

FlightGear has a low bandwidth "command" (aka telnet/props) interface where you can interactively (or automatically via an external program) examine and modify just about any internal variable in the sim. This gives you a great capability to do external scripting, external operator GUI's, etc. For instance, if you have your own GUI for operating the sim and want to use it to set weather conditions, you can leverage the FlightGear telnet interface to have your own program remotely configure the environmental settings in all your FlightGear based visual channels.

Many things (view parameters, weather, time of day, etc.) are configurable via the property system. For things like weather and view selection which don't change rapidly, you can send over new property values using the "telnet" interface. For things that could change rapidly (like view position/orientation) you probably want to blast over UDP packets at 60hz or whatever your screen refresh rate.

The telnet server can be activated with the --telnet=port command line option, where port is the number of the listening port that will be opened locally by the FlightGear fgfs process (note that the --props parameter is equivalent).

The --telnet= option can be used to accept property configuration changes (things like weather effects and time of day.) You can use this interface to read/write individual properties. You could set up a separate gui configured to know the ip addresses of all the slaves so it could update them appropriately to make a change. For the time of day, this presupposes that all the computer clocks are pretty closely in sync ... then you can send the same time offset to them (in seconds) so they all can display the same time of day effects.

A connection to the running server can be established using a conventional telnet client or by opening a simple TCP socket from a custom program. Using a telnet client (such as Putty for Windows) it is possible to connect to a running FlightGear process and issue commands that FlightGear then runs internally. The telnet client can be running on the same machine as FlightGear itself, or on other machines that are in some way connected to the same network (LAN, WAN/internet).

This can for example be used to read and set values within the Property Tree structure, but it can also be used for running fgcommands. A list of available fgcommands is available in $FG_ROOT/Docs/README.commands. Additional fgcommands can be found in $FG_ROOT/keyboard.xml (looking for stuff in <command> tags yields e.g. "reset" and "pause").

This makes it possible to "remote control" FlightGear by using the telnet protocol. This can for example be used to change environment settings (weather, time, visibility etc). But you can also remotely fail equipment: Use the telnet/http interface to unset the "serviceable" flags on the affected instruments and systems. All you need to write is a little script (perl,python, netcat) to do the 25-to-1 multiplex/demultiplex operation.

Many internals are exposed through the property tree, which is accessible in many ways. There is a http and a telnet interface ready for use. A powerful thing is the interface with a freely configurable protocol to send and receive properties. This interface can talk via TCP or UDP.

So you need some kind of GUI to trigger the failures, a layer that translates this to FlightGear properties and a communication layer that communicates with the FlightGear instances. This is an independent application, so you can write it in C, C++, C#, Java, PHP or whatever you like, but you might keep cross platform usability in mind.

Probably not every aircraft developer has implemented every failure feature in his aircraft, since most of them are happy if the "normal procedures" work. Also, some systems are implemented, some are not. So for most GA aircraft for example, the landing gear has no model for the hydraulic system but a simple "if you operate the gear lever, the gear is extending" functionality.

But a telnet connection can also be used for inserting scenery objects or AI aircraft models dynamically. It is also possible to trigger arbitrary Nasal code by writing to a property with a registered Nasal listener. In fact, it is even possible to insert and run Nasal code via telnet, by writing the code to a property and call()'ing it then [1][2].

Information on nearby multiplayer entities is provided in the property tree so you can access this information at least within FlightGear. You could query this data via the property interface, or build some sort of nasal script.

Multiple connections are possible at the same time. For an example in Java please refer to $FG_SRC/scripts/FGClient.

For shell scripting purposes, netcat also works well.

The routines already support network byte order, however there are cases (i.e. interfacing to an external Perl script using pack/unpack, where network byte order is not desirable).

In comparison to the binary native protocol, the props/telnet protocol is a lower bandwidth, high reliability channel. You are guaranteed that every message gets to the receiver once (and only once.) This is great for setting up weather conditions, time of day, camera parameters, and anything else that might have an impact on the visuals. We have a convenient "telnet" interface to all the internal properties and built in commands. Anything you can set from the keyboard, or mouse, or GUI, you can do from a remote program or script. It's much lower bandwidth, but very convenient.

The telnet mechanism is not designed to be high bandwidth. I believe it runs at 5hz? and only processes one line/command per iteration. You could try upping the frequency via the command line, "--props=socket,bi,20,,5500,tcp" (we could change PropsChannel::foundTerminator() to handle several commands per line. Commands could be separated with semicolons. Or perhaps some commands could take more than one argument, eg "get /foo/bar /foo/baz"). For higher bandwidth needs, it would work better to send over an FGNetFDM structure (src/Network/net_fdm.hxx) via UDP or some other custom structure.

A look at the sources shows that a fixed polling interval is used for telnet - default is 5Hz. So it cannot process more than 5 commands per second. That's why it's slow. There's better methods of implementing socket communication instead of polling, but I haven't looked into the module and don't know why this was chosen. The polling interval is configurable though - so you can speed it up. Use:

     fgfs ..... --telnet=medium,direction,HZ,localhost,PORT,style

Use HZ to select the polling frequency (e.g. "100") and PORT for the telnet port (e.g. 9999). The other parameters are unused (it seems) when using the telnet protocol. Probably there for historic reasons (?). Maybe Curt knows. Remember you have to specify 6 parameters separated by a "," - otherwise you cannot configure the polling frequency. So call something like:

     fgfs ..... --telnet=foo,bar,100,foo,9999,bar

TCP sockets are easier to implement for almost all applications, precisely because they are reliable. UDP will only look easier if you plan on skipping the code to recover from a lost packet. Some applications can do this, but the property tree can't -- property access is non-idempotent in the general case. Listeners can have side effects.

Telnet Server

FlightGear has a telnet server that can be used to read and set values of the Property Tree. The telnet server is activated with the --telnet=port command line option, where port is the number of the listening port that will be opened.

A connection to the server can be done using a telnet client or opening a simple socket from any program. Multiple connections (more below) are possible at the same time. Too start FG and open the port 5401 as available telnet server use this command:

fgfs --telnet=5401

Using this server protocol is covered in detail on the Telnet usage page.

Starting FlightGear with Telnet Server

(Note if you want to use an SSH client like putty, you'll need to disable the SSH mode, FlightGear does NOT support SSH, it only supports the raw telnet mode!)

Now, start up FlightGear with the following option: --telnet=5401 (or pick your favorite port number) Once FlightGear is up and running, go to an xterm/shell on the same machine running FlightGear and type "telnet localhost 5401". You could do this from any machine on the net by substituting the appropriate machine name/IP-address. From the telnet session hit enter or type "help" to get a list of available commands. You can now remotely navigate through the property structure and examine and change values in the live running copy of FlightGear. This is very powerful (although low bandwidth) and allows you to set and change a huge range of values in FlightGear.

Even better, it's pretty easy to automate a remote telnet session so you can write scripts or code that can remotely control FlightGear in various ways. I've written a commercial operator GUI that interfaces to FlightGear in this way. I've scripted (Perl) out all the FAA Level 3 FTD certification tests in this way (setting up initial conditions, resetting the FDM, enabling/adjusting autopilot modes, and even remotely manipulating control surfaces when needed.)

And you can use the same mechanism to adjust view parameters, configure weather settings, time of day, etc.

The only draw back is that it's low bandwidth. It's good for setting a few parameters at a relatively low rate, it's good for interactive use, it's good for many remote scripting tasks, but you don't want to use it to try to blast a bunch of position/orientation data in real time. We have other interfaces better suited for that.

I should also point out that we have an html version of the telnet interface "--httpd=5400" that allows you to connect up to a live copy of FlightGear from any web browser and interact with the sim. Just type in a URL like this: "http://localhost:5401/";

Connect with Telnet Client

Once connected to the server using a telnet client, you will be given a prompt such as if you where connected to a Unix telnet server, example:

francesco@francesco-desktop:~$ telnet 127.0.0.1 5401
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

Where 127.0.0.1 is the ip address (127.0.0.1=localhost) of the running FlightGear server and 5401 is the local server port (incoming) set when FlightGear started.

If you are connecting on a Windows machine, you may find that you have to specify the hostname of the PC explicitly. For example on a PC which the Windows network identifies as "albatross":

C:\>telnet albatross 5401

This may be necessary for both the Windows command line client as well as when using other clients like Putty. The first time you connect, Windows will prompt you to allow the connection.

Now that you are inside a telnet session, you can start issuing commands (see below) to FlightGear. Generally commands are used to get, change and navigate the values of the property tree, similar to navigating with directories and files.

Prompt

Since we are simulating navigation through a file system, you will be given a prompt which consists of two parts:

  1. current tree position
  2. (the characters >(space))

so you will typically see something like the following:

/>
/autopilot[0]>
/autopilot[0]/settings[0]>

Commands

The following command are available:

  • cd - change directory
  • data - switch to data mode
  • dump - dump to xml
  • get - show a property value
  • show - same as get
  • help - show available commands
  • ls - list directories and paramaters
  • prompt - switch to interactive mode
  • pwd - display current path
  • quit - terminate connection
  • run - run a built in command
  • set - set value of property
  • nasal - run nasal function
  • subscribe and unsubscribe - subscribing to property changes
  • ? - shortcut to help

cd

Change the current working directory; it needs a parameter which is the destination directory. Example session.

/> cd gear
/gear[0]>
/gear[0]> cd launchbar
/gear[0]/launchbar[0]> cd ..
/gear[0]> cd ..
/> cd gear/launchbar
/gear[0]/launchbar[0]> cd /
/>

data

Switch to data mode. This removes the prompt/>. This is mostly useful when communicating from code, e.g. a socket.

To switch back on see prompt below.

dump

dumps the current state in PropertyList-encoded XML. This requires at least a directory argument. (Note that there are some known issues/restrictions [3]).

/> dump postion/
-ERR "node 'postion/' not found"

/> dump /position 
<?xml version="1.0"?>
<PropertyList>
  <longitude-deg type="double">-122.3577761</longitude-deg>
  <latitude-deg type="double">37.61378776</latitude-deg>
  <altitude-ft type="double">28.25613943</altitude-ft>
  <ground-elev-m type="double">-0.131186098</ground-elev-m>
  <ground-elev-ft type="double">-0.4304005839</ground-elev-ft>
</PropertyList>

get

Shows the value of a given parameter, for example, to get the magnetos status inside the controls/engines/engine directory:

/> cd controls/engines/engine
/controls[0]/engines[0]/engine[0]>  get magnetos
magnetos = '3' (int)
/controls[0]/engines[0]/engine[0]> 

or from the root simply:

/> get controls/engines/engine/magnetos
controls/engines/engine/magnetos = '3' (int)
/>

Usually, you'd use "polling" to continuously update a property via "get" - however, as of FlightGear 2.8 there are two new commands supported, see Telnet usage#subscribe and unsubscribe.

help

Show all the available commands, same as just ?

ls

List all the directories and properties within and under the current directory, for example:

/> cd orientation
/orientation[0]> ls
heading-deg =	'70.62909768'	(double)
alpha-deg =	'-176.8560635'	(double)
pitch-rate-degps =	'3.347789774e-07'	(double)
/orientation[0]> 

or with a directory argument

/> ls /orientation
heading-deg =	'104.6032849'	(double)
alpha-deg =	'-176.864208'	(double)
pitch-rate-degps =	'-0.003761442232'	(double)
/>

prompt

Switch on interactive mode prompt/>. One by default. See data above to switch off.

pwd

Display the current directory i.e. print working directory

quit

Terminate the client connection to server.

run

Run a built in command, the command name is required as an argument.

E.g. to reset the simulator (however you will get disconnected immediately!):

/> run reset 
Connection closed by foreign host.

set

Sets the value of a given property, for example, to set the aileron position inside the /controls/flight directory:

/> cd controls/flight
/controls[0]/flight[0]> set aileron 0.2
aileron = '0.2' (double)
/controls[0]/flight[0]> 

or simply:

/> set /controls/flight/aileron -0.1
/controls/flight/aileron = '-0.1' (double)
/> 

nasal

Starting with version 2018.1 you can run nasal commands via telnet. You need to allow nasal from telnet with the "--allow-nasal-from-sockets" commandline option.

/> nasal
<your nasal code>
##EOF##
/>

The code can be many lines. Before the code lines and after the code lines you should have a new line. "##EOF##" is a special end-of-nasal-marker. E.g. to automatically start the F-16 you could do:

/> nasal
f16.autostart();
##EOF##
/>

subscribe and unsubscribe

Caution  You may want to think about updates that happen every frame but are not significant.

Several properties are constantly re-calculated but vary only at a very high level of precision per frame. One can subscribe constant flood of updates that do not show up in a meaningful way on actual instruments and represent changes in value that are neither visible nor actionable. Fuel is a good example, but also items like the attitude indicator and altimeter.

You can observe these properties easily through a subscription in the telnet interface or by refreshing individual items in the phi interface. Just start the Cessna 172 and set the parking brake.

I think the ability to subscribe to changes at a specific level of precision for updates to each property might prove useful.[1]

Note
Cquote1.png unlike the 'get'/set commands, the 'subscribe' command needs its argument to be an absolute path from the root, for example:
subscribe /sim/time/real/seconds

— Kabuki (Fri Jan 10). Re: Virtual Panel/Control from Web Browser.
(powered by Instant-Cquotes)
Cquote2.png

two important gotchas:

  • The subscribe command does return an echo reply even in data mode. The other non retrieving does not, including unsubscribe(!).
  • The ls command replies with one line per item but nothing that signals that all items are listed. If you want to use ls in data mode you have to set a timeout to the reader. [2]


You'll find two new telnet commands in FlightGear >= 2.8 that provide a very simple "subscription" mechanism, so that you can register listeners instead of using polling to get property updates. Obviously, that makes only sense for non-FDM (non-tied) properties and properties that are not updated at frame rate. But otherwise it can save you some bandwidth and prevent unnecessary polling:

Just connect to fgfs via telnet and issue a "help" command for details:

  • subscribe <property>
  • unsubscribe <property>

For each changed property, the notification response will be in the form of: /some/property=value (terminator)


For example, to subscribe to all properties under /local-weather, use subscribe /local-weather. To subscribe to all properties under /canvas, use subscribe /canvas.

Also see: https://gitorious.org/fg/flightgear?p=fg:flightgear.git;a=commit;h=refs/merge-requests/28

Note that it probably makes sense to use the raw (data) mode. If you still want to support older FG versions, you should probably check the version property first, or simply copy the output from running "help" and checking if it contains the "subscribe" string or not - so that you can fall back to polling instead.

Cquote1.png support for "subscriptions" via telnet is a hack, nothing more - it should have been deprecated by HLA, but so far HLA isn't yet quite ready for showtime unfortunately.

I feel it would be better to teach people how to use HLA to do such things, than adding even more stuff to messy, and conceptually-broken, features like the telnet/props code, or even the multiplayer code for that matter.
The underlying code contains some really weird assumptions and lots of hard-coded restrictions, adding stuff to it is always going to be a compromise.


— Hooray (Sat Jan 11). Re: Virtual Panel/Control from Web Browser.
(powered by Instant-Cquotes)
Cquote2.png

Interfacing with other programs

Using the telnet interface, your own custom client software can request the value of any variable at any time. You can also update the value of any other variable at any time. The overall bandwidth of the telnet interface is not very high though so it's not appropriate for blasting all the flight dynamics data at 60hz for instance, but for your application it probably will work very well ... and I can imagine ways to cheat that would make it look like it was working even better than it actually was. (For instance, you might get some delay if you turn the knob, send the data to FlightGear, and then read the new frequency back from FlightGear, but if you know locally how far your knob turned, you can update your local display immediately, and then sync up with FlightGear at a slower rate.)

The cool thing is that you can easily write scripts to access the --telnet=<port#> interface.

 //* psuedo code
 conn = telnet.connect(127.0.0.1, 5480)
 gear_down = conn.get("/gear/state")
 if gear_down:
   conn.set("/gear/state", "up")

Look under src/scripts and you'll find example code showing you how to do it in Python, Perl and C. The Python/Java ones includes a FlightGear module that makes the telnet part pretty transparent.

There are a couple examples in the scripts section of the FlightGear source code. There is a chunk of Perl (telnet.pl) you can include to make access from a Perl script mostly trivial. I think there is a Java example and perhaps something for Python.

You should have a look at fgfsclient.cxx in the FG/script/exmaple subtree (on the CVS). The script/python subtree has also good examples of what can be done using the telnet protocol. The server side of the "telnet" protocol can be found in the src/Network/props.* files. Also FG must be lauched with --props=5501 to activate the server side. Now you juste have to find the properties that you need and the property browser is handy for that.

Some links to source code examples in various flavours:

You could just as easily interact with a running FlightGear instance using Perl, C, C++, Java, Python, probably even <ACK> visual basic or anything else that can do TCP/IP network communication ... MatLab? netcat? twisted?

Also note that the downside to the telnet interface is that you can't blast a lot of data across it. It's fine if you want to monitor location and speed every second or 1/4 second and occasionally set some values; such as dump in a new weather configuration, reset the aircraft location, or read a set of values, etc.

If you need to track 100 different variables at 60hz, this isn't the interface for you. For that you should consider using a native protocol, implemented in C++ (e.g. FGNetCtrl via UDP).

You could take a look at FlightGear/scripts/example/fgfsclient.c for one example to query the values of a property from FlightGear. It should (in theory) be possible to use the net_fdm and net_ctrls code (which can be found in FlightGear/src/Network) also

Use Case: Instructor Station

I have just added the raw beginnings of a Java client library and trivially-simple Swing GUI demo under scripts/java/demo/FGClient/. Here's the Java code to connect to a FlightGear process and increase the current altitude by 1000 feet:

  FGConnection fgfs = new FGConnection("localhost", 9000);
  double altitude = fgfs.getDouble("/position/altitude-ft");
  fgfs.setDouble("/position/altitude-ft", altitude + 1000);
  fgfs.close();

The demo application displays the current altitude, longitude, and latitude in a small GUI window, and uses a separate thread to update the values every second. To use it, try these commands:

  fgfs --telnet=9000
  java FGFSDemo localhost 9000

I might develop this into a remote instructor's panel, a full configuration GUI, a remote-control module for weather and other environment parameters, or any combination of these. Contributions are welcome, of course.

  set /sim/presets/latitude-deg -9999.0
  set /sim/presets/longitude-deg -9999.0
  set /sim/presets/altitude-ft -9999

  set /sim/presets/airport-id KANE

  set /sim/presets/ndb-freq ""
  set /sim/presets/vor-id ""
  set /sim/presets/airspeed-kt ""
  set /sim/presets/offset-distance ""
  set /sim/presets/offset-azimuth ""
  set /sim/presets/glideslope-deg ""
  set /sim/presets/runway ""
  set /sim/presets/fix ""
  set /sim/presets/ndb-id ""
  set /sim/presets/heading-deg ""
  set /sim/presets/vor-freq ""

  run presets-commit

To start on a 5 mile final to runway KBUR, runway 08 at 90 kt (3° glide slope) you could do something like:

  set /sim/presets/longitude-ft -9999.0
  set /sim/presets/latitude-deg -9999.0
  set /sim/presets/altitude-ft -9999

  set /sim/presets/airport-id KBUR
  set /sim/presets/runway 08
  set /sim/presets/offset-distance 5
  set /sim/presets/glideslope-deg 3
  set /sim/presets/airspeed-kt 90

  set /sim/presets/ndb-freq ""
  set /sim/presets/vor-id ""
  set /sim/presets/offset-azimuth ""
  set /sim/presets/fix ""
  set /sim/presets/ndb-id ""
  set /sim/presets/heading-deg ""
  set /sim/presets/vor-freq ""

  run presets-commit

To start over some fix (DAVID for instance) :-) at an altitude of 5000 ft, airspeed of 90 kt, and heading of 45°:


  set /sim/presets/latitude-deg -9999.0
  set /sim/presets/longitude-deg -9999.0

  set /sim/presets/altitude-ft 5000
  set /sim/presets/airspeed-kt 90
  set /sim/presets/fix DAVID
  set /sim/presets/heading-deg 45
  set /sim/presets/offset-distance 0

  set /sim/presets/airport-id ""
  set /sim/presets/ndb-freq ""
  set /sim/presets/vor-id ""
  set /sim/presets/offset-azimuth ""
  set /sim/presets/glideslope-deg ""
  set /sim/presets/runway ""
  set /sim/presets/ndb-id ""
  set /sim/presets/vor-freq ""

  run presets-commit

Use Case: Autopilot & Route Manager

The autopilots (there are several different implementations) are part of the models. (Rationale is that autopilot devices normally are mounted *in* the aircraft in the real world =). If you are developing your own stand-alone autopilot and want it on another host, there are several ways to interface to the simulator to control the aircraft remotely.

Everything you need to read and set to control an aircraft is in the aircraft's property tree. (This is how the aircraft is controlled internally too) This property tree can be read and modified (if the property is not a read-only one) across the network.

The simplest way to /set/ a property is perhaps via the telnet interface. fgfs --telnet=<port> then do "telnet <host> <port>" and type "help" to see the syntax. You can read properties as well, that way, but perhaps you rather prefer to get data synchronously. This can be achieved through the general i/o-interface.

Regarding the autopilot, I believe you have to set the target speed value and then activate that particular autopilot mode, so you need to actually set two values. You should be able to look in the autopilot GUI to see the exact property names (I don't have them here off the top of my head.) And watch out for spelling: that's one thing I've learned to check when things don't work as expected. With properties, the compiler can't spell check variable names at compile time so typos can creep in. When something doesn't behave as expected, it's worth looking in the property tree while the code is running to see if any new properties (with similar, but not quite exact spellings) have appeared.

At a lower level, the FlightGear waypoint system (route manager) can handle any arbitrary latitude and longitude and altitude. However, something that might work better for you (?) would be to just set up a basic autopilot that can hold a commanded heading, altitude, speed, etc. and then monitor the flight location from the remote search algorithm and pass heading updates to the autopilot.

The autopilot target values are all stored in the property system, so it's really straightforward to change them from a remote application using the "telnet" interface. And likewise you could monitor aircraft location through the same interface.

There are commands available for clearing the list, removing entries, etc.

  @clear             ... clear route
  @delete3           ... delete 4th entry
  @insert2:ksfo@900  ... insert "ksfo@900" as 3rd entry
  ksfo@900           ... append "ksfo@900"
  @activate          ... activate route
  @next              ... go to next entry
  @previous          ... go to previous entry
  @jump3             ... go to 4th entry
  @save              ... save route to the file indicated in /autopilot/route-manager/file-path
  @load              ... load route from the file indicated in /autopilot/route-manager/file-path

For example set /autopilot/route-manager/input @clear

This works also from the property browser, or via Nasal etc. The route manager dialog uses the same interface.

Of course, you have to use an autopilot which takes the waypoints from the route manager if you want your aircraft flown through all the points. The default AP does this.

Everything that you can do in the "Route-Manager" dialog can be done via any way to write to properties. The dialog itself does exactly that. There's a command property /autopilot/route-manager/input. You can send commands to add/insert/remove/pop waypoints from the list, which you can see in the dialog. Here's the syntax description from $FG_ROOT/gui/dialogs/route-mgr.xml and src/Autopilot/route_mgr.cxx:

command interface /autopilot/route-manager/input:

  @clear             ... clear route
  @pop               ... remove first entry
  @delete3           ... delete 4th entry
  @insert2:ksfo@900  ... insert "ksfo@900" as 3rd entry
  ksfo@900           ... append "ksfo@900"

Use Case: Radio Stack

For the easiest start, I'd use a single fg instance, with the telnet server enabled, and write a little (e.g. Perl) script that reads the parallel port where you have your switch connected, and changes a property via the telnet interface when the switch gets opened/closed. If you'd like to build a hardware radio stack and interface it to FlightGear. There are a couple of ways you could attack this.

  • Add a module (i.e. some code) inside FlightGear that runs every frame. Your code would read all your hardware switches through whatever mechanism you have devised and update the FG internals. It would also send things like frequencies (probably cooked up in the best flavor for your hardware) so that the radio stack can display the frequencies.
  • You could go for total separation and create an external application that talks directly to your hardware. That application could then communicate with FlightGear via the "telnet" interface to read the necessary FlightGear property values to update your hardware display, and write the switch/knob values back to FlightGearas input to it's radio models.

There are probably a variety of other ways you could get this done, but these two approaches are the ones that jump to the front of my list. The first approach would require a bit more digging into the FlightGear internals, the 2nd approach could have potentially sluggish performance. The "telnet" interface is the ultimate in flexibility, but is not anywhere close to high bandwidth.

Related content

Source code