Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 6 of 6

Thread: Communicating with a Telnet server

  1. #1
    Junior Member
    Join Date
    Oct 2012
    Posts
    3
    Thanks
    1
    Thanked 0 Times in 0 Posts

    Default Communicating with a Telnet server

    Hi,

    I'm quite new to network programming in general and I patched together some code which requests data from a server and prints that date. The code works but it doesn't look like a clean solution to me. Can I simplify the code? Especially removing the never stopping while loop and all those "else if" with the "writer.flush" look a bit redundant.
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.Calendar;
     
    public class TelnetIO {
     
    	private static final String END_KEY = "$$EOE";
    	private static final String START_KEY = "$$SOE";
    	private final static String CMD_EXIT = "exit\r\n";
    	private String host = "horizons.jpl.nasa.gov";
    	private int port = 6775;
    	private String date;
     
    	public static void main(String[] args) {
    		TelnetIO socket1 = new TelnetIO();
    		System.out.println("The final result is:\n"+socket1.getData(399));
    	}
     
    	private TelnetIO() {
    		Calendar today = Calendar.getInstance();
    		date = today.get(Calendar.YEAR)+"-"+(today.get(Calendar.MONTH)+1)+"-"+today.get(Calendar.DAY_OF_MONTH);
            System.out.println("Current date: "+date);
    	}
     
    	public String getData(int objectid) {
    		String strdata = "ERROR";
     
    		try {
    			Socket telnet = new Socket(host, port);
    			telnet.setKeepAlive(true);
    			PrintWriter writer = new PrintWriter(telnet.getOutputStream());
    			InputStream reader = telnet.getInputStream();
    			StringBuilder sb = new StringBuilder();
    			byte[] buffer = new byte[8192];
    			int len;
    			boolean bconnected = false;
     
    			while ((len = reader.read(buffer)) != -1) {
    				String readchar = new String(buffer, 0, len);
    				sb.append(readchar + "\n");
    				//System.out.println(readchar);
     
    				if (bconnected && readchar.contains(START_KEY) && readchar.contains(END_KEY)) {
    					strdata = readchar.substring(readchar.indexOf(START_KEY)+START_KEY.length(), readchar.indexOf(END_KEY)).trim();
    				}
     
    				if (readchar.contains("[E]phemeris")) {
    					writer.print("E\r\n");
    					writer.flush();
    				}
    				else if (readchar.contains("[N]ew-case")) {
    					writer.print("N\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("observe")) {
    					writer.print("v\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("coordinate center")) {
    					writer.print("500@0 \r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("starting")) {
    					writer.print(date+" 00:00\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("ending")) {
    					writer.print(date+" 00:01\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("output interval")) {
    					writer.print("1m\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("confirm") ||
    						readchar.toLowerCase().contains("accept default output") ) {
    					writer.print("y\r\n");
    					writer.flush();
    				}
    				else if (readchar.toLowerCase().contains("reference plane")) {
    					writer.print("eclip\r\n");
    					writer.flush();
    				}
    				else if (readchar.trim().endsWith("Horizons>")) {
    					if(!bconnected) {
    						writer.print(objectid+"\r\n");
    						bconnected = true;
    					}
    					else {
    						writer.print(CMD_EXIT);
    						writer.flush();
    					}
    					writer.flush();
    				}
    				else if (bconnected) {
    					//System.out.println("Unknown line: "+readchar);
    				}
     
    			}
    			telnet.close();
    		} catch (SocketException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
     
    		//We only want line 2, 3 and 4
    		return strdata.split("\n")[1]+strdata.split("\n")[2]+strdata.split("\n")[3];
    	}
    }

    EDIT:Some nice highlighting
    Last edited by EoD; October 8th, 2012 at 11:52 AM.


  2. #2
    Member
    Join Date
    Jun 2012
    Location
    Left Coast, USA
    Posts
    451
    My Mood
    Mellow
    Thanks
    1
    Thanked 97 Times in 88 Posts

    Default Re: Communicating with a Telnet server

    Quote Originally Posted by EoD View Post
    ...Especially removing the never stopping while loop
    Question: What do you think is the purpose of the never-stopping while loop?

    Answer: Keeps trying to read from socket as long as the socket connection is maintained.

    Question: How does it ever leave the never-stopping while loop?

    Answer: After getting the last thing that the program is looking for, the program sends an "exit" command to the server's "Horizons>" application on this telnet port.

    Then the server takes down the socket connection and the program detects that, leaves the loop, and prints the significant part of the payload and terminates gracefully. Of course if some network problem causes the socket connection to be broken (or prevents establishment of the connection in the first place), the program also exits that non-stopping loop and terminates (but maybe with an exception, since the program doesn't actually test for satisfactory retrieval of the information).

    So: What would be your way of doing something other than the non-stopping while loop?

    Well, maybe the program could be changed to implement some kind of state machine that depends on the sequence of messages from the server rather than just looking for particular substrings without regard to sequence, but would that be simpler to implement? Maybe yes or maybe no.

    I do think that it might more elegant and maybe more straightforward to document and to modify if the server ever changes its tune. This might be worth pursuing if you are really interested. Once you get into the "state machine" groove, you realize that everything is a state machine. (It's like when you give a kid a hammer: Everything begins looking like a nail!)

    Big complicated sequences (bigger and more complicated than this straightforward stuff) can be broken down into very simple code in the state machine engine. The state transition table (maybe not so simple) that drives the machine might be a head-scratcher, but it will all be in a single location in the program and its documentation. Starting with simple programs like this (especially having one that already works), might be a good training experience for getting into stuff the way the Big Guys do it. (Note that there still has to be some kind of loop that continuously, or at least periodically, checks for socket validity since it is going to be trying to read a stream from the server for the almost the entire time. But so what? What else is the program going to do other than monitoring and acting on responses from the server.)

    Anyhow...

    Quote Originally Posted by EoD View Post
    and all those "else if" with the "writer.flush" look a bit redundant.
    Well, what about all of those if/else things? Maybe you could consider a look-up table inside a function that sees if a received table entry String is a substring of a received string. Then that would allow selection of the appropriate action String. Whenever a match is found, a single place in the program could tack on the "\r\n" thing and do the flush thing.

    Looking at the if-else stuff, it seems that, maybe the table could be a 2-D array of Strings like the following:
            String [][] actionTable = {
                {"[E]phemeris"          , "E"         },
                {"[N]ew-case"           , "N"         },
                {"observe"              , "v"         },
                {"coordinate center"    , "500@0 "    },
                {"starting"             , startDateStr},
                {"ending"               , endDateStr  },
                {"output interval"      , "1m"        },
                {"confirm"              , "y"         },
                {"accept default output", "y"         },
                {"reference plane"      , "eclip"     }
     
            };

    Note that startDateStr and endDateStr are built at run time by appending " 00:00" and " 00:01" to a Date string obtained from Calender methods. That's do-able.

    A function (maybe named String getAction(String promptString, String [][] actionTable)) would step through the table and see if actionTable[k][0] is a substring of the promptString and return the actionTable[k][1] (or some such thing). Will return the action string if a match is found, otherwise, maybe return an empty string. Or some such thing.

    Note that the "Horizons>" prompt doesn't fit readily into this scheme since there are two different actions, based on whether it has been seen before during this session. Furthermore, a state variable, bconnected, is changed, depending on whether or not this is the first time. This variable affects things other than just the action that should result from finding the "Horizons>" prompt.

    So, maybe the test for "Horizons>" would be done separately, as it is now. (Or you could, maybe, "enhance" the table lookup function to take care of bconnected.)

    Note that it's not a simple case of pattern matching, since idea is that the first entry is a substring of the server's response that determines what the program should do. (Otherwise you could make a more sophisticated HashTable<String, String> to do the deed in a single function call rather than having to step through a 2-D array.)

    Since there are a grand total of eleven or twelve action Strings, implementation efficiency is hardly an issue (not to me, at least), but some might prefer the elegance of a lookup table rather than a sequence of a dozen or so if-else statements.

    My opinion on that: Chacun à son goût!



    A final note: Your ability to "patch together some code" that results in a non-trivial living, breathing, working program involving network sockets is an outstanding effort! It presents a VSLO (Very Special Learning Opportunity---I like making up abbreviations like this, but some people think I'm just being silly. Oh, well...).

    So...

    Experiment. Go for a Better Mousetrap (I was going to abbreviate Better Mousetrap too, but...)


    Cheers!

    Z
    Last edited by Zaphod_b; October 8th, 2012 at 04:43 PM.

  3. The Following User Says Thank You to Zaphod_b For This Useful Post:

    EoD (October 8th, 2012)

  4. #3
    Junior Member
    Join Date
    Oct 2012
    Posts
    3
    Thanks
    1
    Thanked 0 Times in 0 Posts

    Default Re: Communicating with a Telnet server

    First of all, a huge thanks for the very detailed answer! I even smiled while reading it

    Quote Originally Posted by Zaphod_b View Post
    Question: What do you think is the purpose of the never-stopping while loop?

    Answer: Keeps trying to read from socket as long as the socket connection is maintained.

    Question: How does it ever leave the never-stopping while loop?

    Answer: After getting the last thing that the program is looking for, the program sends an "exit" command to the server's "Horizons>" application on this telnet port.

    Then the server takes down the socket connection and the program detects that, leaves the loop, and prints the significant part of the payload and terminates gracefully. Of course if some network problem causes the socket connection to be broken (or prevents establishment of the connection in the first place), the program also exits that non-stopping loop and terminates (but maybe with an exception, since the program doesn't actually test for satisfactory retrieval of the information).
    It happened to me that the connection hung or my input was faulty and the program just waited forever. Is there some nice solution apart from running my own timeout timer? (State machine engine?)
    I also get my own inputs in the while loop (the server sending me my own key inputs back) and check for them in the for loop. Is there a more cleaner solution to skip those than to add another boolean and set it true after each "writer.flush()" and set it false in something like "if(bmyowninput) { bmyowninput = false; continue; }" at the beginning of the while loop (see code at the bottom). Actually I could just ignore that part and parse my own input, it should not do much overhead


    Quote Originally Posted by Zaphod_b View Post
    So: What would be your way of doing something other than the non-stopping while loop?

    Well, maybe the program could be changed to implement some kind of state machine that depends on the sequence of messages from the server rather than just looking for particular substrings without regard to sequence, but would that be simpler to implement? Maybe yes or maybe no.

    I do think that it might more elegant and maybe more straightforward to document and to modify if the server ever changes its tune. This might be worth pursuing if you are really interested. Once you get into the "state machine" groove, you realize that everything is a state machine. (It's like when you give a kid a hammer: Everything begins looking like a nail!)
    I will try to read something about a state machine. I heared that word today for the first time and the wikipedia article is a bit lengthy, but it sounds interesting!


    Quote Originally Posted by Zaphod_b View Post
    Well, what about all of those if/else things? Maybe you could consider a look-up table inside a function that sees if a received table entry String is a substring of a received string. Then that would allow selection of the appropriate action String. Whenever a match is found, a single place in the program could tack on the "\r\n" thing and do the flush thing.

    A function (maybe named String getAction(String promptString, String [][] actionTable)) would step through the table and see if actionTable[k][0] is a substring of the promptString and return the actionTable[k][1] (or some such thing). Will return the action string if a match is found, otherwise, maybe return an empty string. Or some such thing.
    I totally like the idea and I implemented it with some for loops. I guess there isn't a short version in java to search a 2d-String-array for a specific String, like I did below?


    Quote Originally Posted by Zaphod_b View Post
    Note that the "Horizons>" prompt doesn't fit readily into this scheme since there are two different actions, based on whether it has been seen before during this session. Furthermore, a state variable, bconnected, is changed, depending on whether or not this is the first time. This variable affects things other than just the action that should result from finding the "Horizons>" prompt.

    So, maybe the test for "Horizons>" would be done separately, as it is now. (Or you could, maybe, "enhance" the table lookup function to take care of bconnected.)
    I'm not sure how to "enhance" the table apart from adding more DUMMYs (e.g. "[CONNECTED]Horizons>" and "[NOTCONNETED]Horizons>") and do some boolean check to skip either the CONNECTED or NOTCONNECTED strings. In the end the program has to connect to the socket once and then execute the chain of commands for multiple "IDs" as indicated by ".getData(399)", so I'll rewrite the program to open the socket only once but play with the bconnected boolean. But that for later


    Quote Originally Posted by Zaphod_b View Post
    Note that it's not a simple case of pattern matching, since idea is that the first entry is a substring of the server's response that determines what the program should do. (Otherwise you could make a more sophisticated HashTable<String, String> to do the deed in a single function call rather than having to step through a 2-D array.)
    It actually might be more simple just to use the full strings, but I'm a bit afraid of invisible characters and non-portability of the code if I use the full String as seen on the command prompt. Seems like windows and linux for instance receive not quite the same characters (or at least don't show the same).


    Here's the new state-less code:
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.Calendar;
     
    public class TelnetIO {
     
    	private static final String END_KEY = "$$EOE";
    	private static final String START_KEY = "$$SOE";
    	private static final String DATE_DUMMY = "xDATEx";
    	private static final String ERROR = "ERROR";
    	private static final String host = "horizons.jpl.nasa.gov";
    	private static final int port = 6775;
    	private String date;
    	private String[][] actionTable = {
    		{"[e]phemeris"          , "e"                  },
    		{"[n]ew-case"           , "n"                  },
    		{"observe"              , "v"                  },
    		{"coordinate center"    , "@ssb"               },
    		{"starting ct"          , DATE_DUMMY + " 00:00"},
    		{"ending   ct"          , DATE_DUMMY + " 00:01"},
    		{"output interval"      , "1m"                 },
    		{"confirm selected"     , "y"                  },
    		{"accept default output", "y"                  },
    		{"reference plane"      , "eclip"              },
    		{"horizons>"            , "exit"               },
    	};
     
    	public static void main(String[] args) {
    		TelnetIO socket1 = new TelnetIO();
    		System.out.println("The final result is:\n" + socket1.getData(399));
    	}
     
    	private TelnetIO() {
    		Calendar today = Calendar.getInstance();
    		date = today.get(Calendar.YEAR)+"-"+(today.get(Calendar.MONTH)+1)+"-"+today.get(Calendar.DAY_OF_MONTH);
    		System.out.println("Current date: "+date);
     
    		for(int i=0; i<actionTable.length; i++)
    			if(actionTable[i][1].contains(DATE_DUMMY))
    				actionTable[i][1] = actionTable[i][1].replaceAll(DATE_DUMMY, date);
    	}
     
    	public String getData(int objectid) {
    		String strdata = ERROR;
     
    		try {
    			Socket telnet = new Socket(host, port);
    			telnet.setKeepAlive(true);
    			PrintWriter writer = new PrintWriter(telnet.getOutputStream());
    			InputStream reader = telnet.getInputStream();
    			byte[] buffer = new byte[8192];
    			int len;
     
    			boolean bconnected = false;
    			boolean bmyowninput = false;
     
     
    			while ((len = reader.read(buffer)) != -1) {
    				String readchar = new String(buffer, 0, len);
    				//System.out.println(readchar);
     
    				if(bmyowninput) {
    					bmyowninput = false;
    					continue;
    				}
     
    				/* already inside the chain of command? */
    				if(bconnected) {
    					if (readchar.contains(START_KEY) && readchar.contains(END_KEY)) {
    						/* received data */
    						strdata = readchar.substring(readchar.indexOf(START_KEY)+START_KEY.length(), readchar.indexOf(END_KEY)).trim();
    					}
    					else {
    						System.out.println(readchar);
    						/* server probably wants interaction */
    						for(int i=0; i<actionTable.length; i++) {
    							if(readchar.toLowerCase().contains(actionTable[i][0])) {
    								writer.print(actionTable[i][1] + "\r\n");
    								writer.flush();
    								bmyowninput = true;
    								break;
    							}	
    						}
    					}
    				}
    				else if (readchar.trim().endsWith("Horizons>")) {
    					writer.print(objectid + "\r\n");
    					writer.flush();
    					bmyowninput = true;
    					bconnected = true;
    				}
     
    			}
    			telnet.close();
    		} catch (SocketException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
     
    		// We only want line 2, 3 and 4
    		if(strdata.split("\n").length < 4)
    			return ERROR;
    		else
    			return strdata.split("\n")[1] + strdata.split("\n")[2] + strdata.split("\n")[3];
    	}
    }

  5. #4
    Member
    Join Date
    Jun 2012
    Location
    Left Coast, USA
    Posts
    451
    My Mood
    Mellow
    Thanks
    1
    Thanked 97 Times in 88 Posts

    Default Re: Communicating with a Telnet server

    Quote Originally Posted by EoD View Post
    First of all, a huge thanks for the very detailed answer! I even smiled while reading it


    It happened to me that the connection hung or my input was faulty and the program just waited forever. Is there some nice solution apart from running my own timeout timer?
    Handling network timeouts

    Probably the easiest way:
    Default SO_TIMEOUT is zero, which means that reads are blocking. You can use the Socket.setSoTimeout() method to give a maximum delay before returning with a value of zero.

    Quote Originally Posted by EoD
    ...Actually I could just ignore that part and parse my own input, it should not do much overhead
    The original loop simply ignores Strings from the server that don't contain the carefully selected substrings that are considered to be a prompt, so it isn't an issue if you are using the program to execute an automatic play of a previously observed "manual" session. (I assume that you have run a session with your own system's telnet client to see what the server actually gives out at each step.)

    Now about the program itself: I didn't offer criticism of the logic since you said it "works," so, if it suits your purposes, you can use it as is.

    However, there are a number of discrepancies that might cause erratic behavior. (At least that's what I observed.)

    For example the strings from the server aren't "parsed" at all. They are merely examined to see if they contain a particular substring. Case is ignored.

    I'll show an example: The original program, as well as your table-driven version treats any String with the case-ignoring substring "observe" to be the prompt that requires the client response to be the character v.

    The actual prompt is
     Observe, Elements, Vectors  [o,e,v,?] :

    I note that there are a number of strings from the server that contain case-ignoring "observe" but maybe that's OK as long as the substrings are tested in the correct order.

    I think it would be safer to test (at least) that the actual prompt occurs. And maybe, even better, test that it occurs at or near the end of the String, using the substring method that lets the search starting at a particular index value rather than searching the entire String.
    .
    So, for starters, the first String for that table entry should be, perhaps, the actual prompt, not the word "observe." Then, why not force an exact match instead of doing a case-insensitive search? Then think about how to use the length of the prompt to give a starting value for the indexed substring method.

    The table that I showed in my previous post was based on the if-else sequence in the original code, and was intended to be a starting point, not the last word. The idea is: Take something that works. Derive another approach and verify that it works the same way. Then, make whatever functional improvements that you discover the need for as you experiment. Having the prompt strings and response strings together in a table makes it easier to maintain and modify as the need arises.

    The state machine approach would not just search every server string for a certain substring, but would start looking for the "Horizons >" prompt and issue the response.

    Then it would be looking for the "Select ... [E]phemeris, [F]tp, [M]ail, [R]edisplay, ?, <cr>:" prompt and issue its response.

    Etc.

    Since this kind of session is linear (no loops in the state machine), I don't think the state machine approach would, in itself, simplify the logic, but this program could act as a learning tool for situations where the actual sequence is more complicated. Again, having a program that works is a great jumping-off point for further exploration.

    One other thing: The final String, containing the payload, contains three sets of numbers with each set separated by a '\r' character. Maybe these print OK on some systems, but not on my Linux. (The '\r' characters cause only the last line of the three to be printed.)

    So, I changed main() to this:
        public static void main(String [] args) {
            TelnetIO socket1 = new TelnetIO();
            String socketStr = socket1.getData(399);
            String outStr = "";
     
            // Change '\r' to '\n'
            for (int i = 0; i < socketStr.length(); i++) {
                char thisChar = socketStr.charAt(i);
                if (thisChar == '\r') {
                    outStr += '\n';
                }
                else {
                    outStr += thisChar;
                }
            }
     
            System.out.println("The final result is:\n" + outStr);
        } // End of main()

    Cheers!

    Z
    Last edited by Zaphod_b; October 9th, 2012 at 12:36 PM.

  6. #5
    Junior Member
    Join Date
    Oct 2012
    Posts
    3
    Thanks
    1
    Thanked 0 Times in 0 Posts

    Default Re: Communicating with a Telnet server

    Quote Originally Posted by Zaphod_b View Post
    Probably the easiest way:
    Default SO_TIMEOUT is zero, which means that reads are blocking. You can use the Socket.setSoTimeout() method to give a maximum delay before returning with a value of zero.
    Ok, that's what I was looking for!

    Quote Originally Posted by Zaphod_b View Post
    So, for starters, the first String for that table entry should be, perhaps, the actual prompt, not the word "observe." Then, why not force an exact match instead of doing a case-insensitive search? Then think about how to use the length of the prompt to give a starting value for the indexed substring method.
    That's what I meant with the "portability" above. I had the impression that Windows and Linux received different invisible characters at some (not all) lines. It also seemed that at some lines there are some weird spaces being added. I did a full match at the beginning, then tried a match at the End of a line but it didn't work out and I want to have a working code so I switched to the substring match. I'm considering to replace all invisible ascii chars from the to-be-parsed String and trim() it before doing a "full" match.
    Yes, this "observere" I did expand to "observe, elements, vectors" but apparently deleted it again -.-
    I'm using only lower-case matches, because I had so many typos on my matching Strings that I didn't want to worry anymore about them. Changed it back

    Quote Originally Posted by Zaphod_b View Post
    Since this kind of session is linear (no loops in the state machine), I don't think the state machine approach would, in itself, simplify the logic, but this program could act as a learning tool for situations where the actual sequence is more complicated. Again, having a program that works is a great jumping-off point for further exploration.
    I'm still not quite sure how to do this properly, but I'll dig into it once I got more free time.

    Quote Originally Posted by Zaphod_b View Post
    One other thing: The final String, containing the payload, contains three sets of numbers with each set separated by a '\r' character. Maybe these print OK on some systems, but not on my Linux. (The '\r' characters cause only the last line of the three to be printed.)
    I didn't observe any problems on both Debian, Gentoo and Windows while Parsing the String. But it's cleaner to remove the '\r', yeah.
    EDIT: Bah, I already removed the '\r' in the code I use, stupid me! But the problem was the lack of "\n" which made problems

    Almost same code as last time (should I stop posting it?)
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InterruptedIOException;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.Calendar;
     
    public class TelnetIO extends Thread {
     
    	private static final String END_KEY = "$$EOE";
    	private static final String START_KEY = "$$SOE";
    	private static final String DATE_DUMMY = "xDATEx";
    	private static final String strERROR = "ERROR";
    	private static final double[] iERROR = {0,0,0};
    	private static final String HOST = "horizons.jpl.nasa.gov";
    	private static final int PORT = 6775;
    	private static final int TIMEOUT = 5000;
    	private String date;
    	private String[][] actionTable = {
    		{"[E]phemeris"                  , "e"					},
    		{"[N]ew-case"                   , "n"					},
    		{"Observe, Elements, Vectors"   , "v"					},
    		{"Coordinate center"            , "@ssb"				},
    		{"Starting CT"                  , DATE_DUMMY + " 00:00"	},
    		{"Ending   CT"                  , DATE_DUMMY + " 00:01"	},
    		{"Output interval"              , "1m"					},
    		{"Confirm selected station"     , "y"					},
    		{"Accept default output"        , "y"					},
    		{"Reference plane"              , "eclip"				},
    		{"Horizons>"                    , "exit"				},
    	};
     
    	public static void main(String[] args) {
    		TelnetIO socket1 = new TelnetIO();
    		String data = socket1.getData(399);
    		System.out.println("The final result is:\n" + data);
    		/* ignore next line, just for completeness :) */
    		System.out.println("The z coordinate is: " + transformIntoCoords(data)[2]);
    	}
     
    	private TelnetIO() {
    		Calendar today = Calendar.getInstance();
    		date = today.get(Calendar.YEAR)+"-"+(today.get(Calendar.MONTH)+1)+"-"+today.get(Calendar.DAY_OF_MONTH);
    		System.out.println("Current date: "+date);
     
    		for(int i=0; i<actionTable.length; i++)
    			if(actionTable[i][1].contains(DATE_DUMMY))
    				actionTable[i][1] = actionTable[i][1].replaceAll(DATE_DUMMY, date);
    	}
     
    	public String getData(int objectid) {
    		String strdata = strERROR;
     
    		try {
    			Socket telnet = new Socket(HOST, PORT);
    			telnet.setKeepAlive(true);
    			telnet.setSoTimeout(TIMEOUT);
    			PrintWriter writer = new PrintWriter(telnet.getOutputStream());
    			InputStream reader = telnet.getInputStream();
    			byte[] buffer = new byte[8192];
    			int len;
     
    			boolean bconnected = false;
     
    			try {
    				while ((len = reader.read(buffer)) != -1) {
    					String readchar = new String(buffer, 0, len);
    					System.out.println(readchar);
     
    					/* already inside the chain of command? */
    					if(bconnected) {
    						if (readchar.contains(START_KEY) && readchar.contains(END_KEY)) {
    							/* received data */
    							strdata = readchar.substring(readchar.indexOf(START_KEY)+START_KEY.length(), readchar.indexOf(END_KEY)).trim();
    						}
    						else {
    							//System.out.println(readchar);
    							/* server probably wants interaction */
    							for(int i=0; i<actionTable.length; i++) {
    								if(readchar.contains(actionTable[i][0])) {
    									writer.print(actionTable[i][1] + "\r\n");
    									writer.flush();
    									break;
    								}	
    							}
    						}
    					}
    					else if (readchar.trim().endsWith("Horizons>")) {
    						writer.print(objectid + "\r\n");
    						writer.flush();
    						bconnected = true;
    					}
     
    				}
    			} catch (InterruptedIOException e) {
    				System.out.println("Timeout reached. Cancelling!");
    			} finally {
    				telnet.close();
    			}
    		} catch (SocketException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
     
    		// We only want line 2, 3 and 4
    		if(strdata.split("\n").length < 4)
    			return strERROR;
    		else
    			return strdata.split("\r\n")[1] + "\n" + strdata.split("\r\n")[2] + "\n" + strdata.split("\r\n")[3];
    	}
     
    	/* ignore the following code - not network related */
    	static double[] transformIntoCoords(String data) {
    		String[] values = data.split("\n");
    		System.out.println("transformIntoCoords() values[0]: '"+values[0]+"'");
    		String[] coords = values[0].trim().replaceAll("[ ]+", " ").split(" ");
    		if(coords.length < 3) {
    			System.out.println("transformIntoCoords() - ERROR - coords.length < 3");
    			return iERROR;
    		}
     
    		double[] xxx = iERROR;
    		for(int i=0; i<xxx.length; i++)	{
    			System.out.print("transformIntoCoords() coords["+i+"]: '"+coords[i]+"'  -->  ");
    			try {
    				xxx[i] = Double.parseDouble(coords[i]);
    			} catch (NumberFormatException e) {
    				System.out.println("Error parsing value");
    				xxx = iERROR;
    				break;
    			}
    			System.out.println(xxx[i]);
    		}
    		return xxx;
    	}
    }
    Last edited by EoD; October 11th, 2012 at 04:54 PM.

  7. #6
    Member
    Join Date
    Jun 2012
    Location
    Left Coast, USA
    Posts
    451
    My Mood
    Mellow
    Thanks
    1
    Thanked 97 Times in 88 Posts

    Default Re: Communicating with a Telnet server

    Quote Originally Posted by EoD View Post
    ...I had the impression that Windows and Linux received different invisible characters at some (not all) lines.
    I ran the program as well as manual telnet sessions from a Linux command line and piped the output to "tee" so that I could capture everything in a file.

    java TelnetIO | tee TelnetIO.out

    And

    telnet horizons.jpl.nasa.gov 6775 | tee telnet.out


    Some telnet clients may have a "log" mode that captures output, but "tee" works for me.


    The point being that I could look at the output files without depending on pasting stuff from the console window. (There is a lot of stuff that gets overwritten as well as a few non-printing chars that I wanted to examine.) Also I could easily paste all of the complete server prompts into my code to eliminate those annoying typos that might tempt me to take shortcuts (like using case-insensitive abbreviated terms).

    There are a number of places where stuff coming from the server don't remain on the screen because of some ANSI escape sequences. Most of the lines have "\cr\nl" at the end of the lines, but some of them have "\cr\cr\nl"

    Because of the way that the code in getData splits the payload and creates the "Final Result" the lines are separated by '\cr' with no '\lf' So in order to make sure I see them on the screen as well as in the capture file, my main() looks something like
        public static void main(String[] args) {
     
            TelnetIO socket1 = new TelnetIO();
            String retStr = socket1.getData(399);
            // retStr has three lines separated by '\r'.
            // Change them to '\n' so that they display
            // OK on Linux
            // Could replace them by "\r\n" if your operating
            // system likes DOS stuff.
            String finalStr = retStr.replace("\r", "\n");
            System.out.println("The final result is:\n"+finalStr);
     
        }

    At the beginning of readData, it looks like this
                while ((len = reader.read(buffer)) != -1) {
                    //System.out.printf("len = %d\n", len);
                    String readchar = new String(buffer, 0, len);
                    System.out.printf("From server: %s (", readchar);
                    // Uncomment this if you can't look at the file with
                    // a hex editor and you want to see the ANSI escape
                    // sequences and the handful of Unicode chars at the start
                    //for (int i = 0; i < len; i++) {
                        //System.out.printf("%02x ", buffer[i]);
                    //}
                    System.out.printf(")\n");


    Quote Originally Posted by EoD
    Almost same code as last time (should I stop posting it?)
    No need to post more code unless you get stuck. I am interested in your progress, but I am not sure how much more I can help. I'm thinking that you are going great guns!


    Cheers!

    Z

Similar Threads

  1. Handle Telnet Session Progmatically
    By Rihx in forum Java Networking
    Replies: 5
    Last Post: April 15th, 2012, 06:45 PM
  2. [ASK] Telnet and Java
    By ragilsms2746 in forum Java Theory & Questions
    Replies: 1
    Last Post: October 27th, 2011, 05:51 AM
  3. Java Child process cannot execute properly for telnet command
    By mamunbu in forum What's Wrong With My Code?
    Replies: 1
    Last Post: July 25th, 2011, 07:43 AM
  4. Autocomplete over telnet connection
    By April in forum Java Networking
    Replies: 6
    Last Post: June 17th, 2010, 08:58 AM
  5. opening Telnet Command Session
    By voyager in forum Java Networking
    Replies: 3
    Last Post: June 23rd, 2009, 10:34 AM