Concurrency and stream reading in a project for a simple MUD client is proving a bit of headache, so I'm trying to find an alternative. The tee command looks to fit the bill for concurrently splitting output between a file and the terminal. How do I then send messages through the telnet session?
Splitting remote output between the console and a file:
thufir#dur:~/NetBeansProjects/TelnetConsole$
thufir#dur:~/NetBeansProjects/TelnetConsole$ telnet rainmaker.wunderground.com 3000 | tee out.txt
Trying 38.102.137.140...
Connected to rainmaker.wunderground.com.
Escape character is '^]'.
------------------------------------------------------------------------------
* Welcome to THE WEATHER UNDERGROUND telnet service! *
How do I then pipe or somehow send Java messages to the system telnet client? Or, perhaps, would it make more sense to use exec and start the telnet session, and tee, from within Java? Just looking for a good approach.
Yes, exec sounds like a better solution since you want to control both input and outpud data. And do you really need tee in when you use exec, I don't understand why...
Also take a look at netcat instead of telnet - telnet has some special character handling that could give you trouble if you plan to send binary data.
By no means functioning code, but definitely simpler than using Apache TelnetClient. However, it's not possible to use the pipe command(?) with java exec:
package exec;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
class Ideone {
public static void main(String[] args) throws IOException {
new Ideone().start();
}
public void start() throws IOException {
String[] s = new String[6];
s[0] = "telnet";
s[1] = "rainmaker.wunderground.com";
s[2] = "3000";
s[3] = "|";
s[4] = "tee";
s[5] = "out.log";
for (int i=0;i<s.length;i++ ) {
System.out.println(s[i]);
}
Process process = Runtime.getRuntime().exec(s);
OutputStream stdin = process.getOutputStream();
InputStream stderr = process.getErrorStream();
InputStream stdout = process.getInputStream();
read();
write(stdout);
}
private void parseLog() {
//read the log file, automate responses
}
private void write(InputStream stdin) throws UnsupportedEncodingException, IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(stdin, "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
private void read() {
Thread read = new Thread() {
String command = null;
#Override
public void run() {
BufferedReader bufferedInput = new BufferedReader(new InputStreamReader(System.in));
do {
try {
command = bufferedInput.readLine();
} catch (IOException ex) {
System.out.println(ex);
} finally {
}
} while (true);
}
};
read.start();
}
}
Related
I implemented a simple HTTP Server that once calling an endpoint, it will retrieve a text file from S3 and returns it. Unfortunately, when the requests are high, like 100 times per second, it goes to unstable state and can't respond to HTTP requests and they go to timeout. For accessing the S3, I'm run a bash command (which one command takes 2s to execute), so one thought I have is maybe the frequent bash commands and pipes is not efficient. And to add, we are running the code in kubernetes in case it might also cause resource restrictions, etc.
What is the problem? Is it the frequent pipes, HTTP Server API, etc? How to address it?
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class FileRetriever implements Runnable {
HttpServer server;
ProcessBuilder processBuilder;
public FileRetriever() {
processBuilder = new ProcessBuilder();
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
server = HttpServer.create(new InetSocketAddress(serverPort), 0);
server.createContext("/getfile", new GetFileHandler());
server.setExecutor(null);
server.start();
} catch (IOException e) {
logger.error("HttpServer ERROR", e);
}
}
class GetFileHandler implements HttpHandler {
#Override
public void handle(HttpExchange he) throws IOException {
// parse request
// http://localhost:9003/getfile?id=xxxxx
String response = "";
URI requestedUri = he.getRequestURI();
String query = requestedUri.getRawQuery();
String pair[] = query.split("=");
String id="";
if (pair[0].equals("id")) {
id = pair[1];
try {
String command = "aws --endpoint-url="+S3server+" s3 cp s3://file-store/"+id+"/files/"+id+".txt -";
processBuilder.command("sh", "-c", command+" ; true");
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader readerErrors = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
while ((line = reader.readLine()) != null) {
response+=line+"\n";
}
if(response.trim().equals("")) {
response="Not Found";
he.sendResponseHeaders(404, response.length());
}
else {
logger.info("Received file successfully: " + id);
he.sendResponseHeaders(200, response.length());
}
} catch (Exception e) {
response = "ERROR in GetFileHandler.";
}
OutputStream os = he.getResponseBody();
os.write(response.toString().getBytes());
os.close();
}
}
}
}
I tried to pass a UTF-8 String through a Java Socket.
The String contains a mix of English and Greek.
My problem is that when the message passes through the socket all Greek characters turn to "?".
I already tried to set the InputStream character set to UTF-8.
Bellow is my attempt, any help will be appreciated.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) {
try {
String msg = "This is a test - Αυτο ειναι μια δοκιμη";
ServerSocket serverSocket = new ServerSocket(9999);
Thread host = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
Socket socket = serverSocket.accept();
if (socket != null) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
while (true) {
String line = bufferedReader.readLine();
if (line != null) {
System.out.println(line);
} else if(bufferedReader.read() < 0) {
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
host.start();
Socket socket = new Socket("127.0.0.1", 9999);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
printWriter.println(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Edit 1
I run and build my code through IntelliJ Idea and that is where I found the problem.
But after #Ihar Sadounikau comment I updated and my JDK and tried to build and run through PowerShell but still the problem persists.
And this is my result
& 'C:\Program Files\Java\jdk-13.0.2\bin\java.exe' Main
This is a test - ??τ? ε??α? ??α δ?????
With this line: PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true); you are converting a bytestream (i.e., InputStream / OutputStream into a charstream (i.e., Reader / Writer). Anytime you do that, if you fail to specify the encoding, you get platform default, which is unlikely what you want.
You (and #IharSadounikau) are seeing different results, because the 'platform default' is switching around on you. It's one of the reasons you REALLY do not want to use it, ever. Figuring out that your code has the bug where it only works if your platform default encoding is the same as the person who developed it – is generally untestable.
Try new PrintWriter(socket.getOutputStream(), true, StandardCharsets.UTF_8).
Maybe this will help:
String msgEncode = URLEncoder.encode(msg, "UTF-8");
printWriter.println(msgEncode);
And:
String line = bufferedReader.readLine();
String msgDecode = URLDecoder.decode(line, "UTF-8");
I'm trying to write a Java program to run terminal command. Googling and SO got me to here:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Detector {
public static void main(String[] args) {
String[] cmd = {"ls", "-la"};
try {
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) !=null){
System.out.println(line);
}
p.waitFor();
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
So far, so good. The problem is if I try to run a command like "python -V" via
String[] cmd = {"python", "-V"};
The program will run, but no output is actually printed out. Any ideas?
The output you see on your command line when running python -V is actually being printed to standard error. To capture this, you need to use a different InputStream, such as this:
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(p.getErrorStream()));
The rest of your code is fine.
I'm trying to connect my Tivo over telnet, using Java.
Here's my little test script so far.
package tivotelnettest;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class TivoTelnetTest
{
/***
* Main for the TelnetClientExample.
***/
public static void main(String[] args) throws Exception
{
// Create object of Socket.
Socket socket = new Socket("192.168.0.10", 31339);
// The command.
String command = null;
// Create object of Input Stream to read from socket.
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
// Create object of Output Stream to write on socket .
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
// Object of Buffered Reader to read command from terminal.
BufferedReader buffRead = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Welcome to Telnet Client");
System.out.println("<Telnet Prompt>");
System.out.println("Waiting for command: ");
command = buffRead.readLine();
while(!"EXIT".equals(command)){
System.out.println("Sending command: " + command);
dataOutputStream.writeChars(command);//sends command to server
System.out.println("Response: " + dataInputStream.readLine()); //gets the response of server
System.out.println("Waiting for command: ");
command = buffRead.readLine();
}
socket.close(); //close port
dataInputStream.close(); //close input stream
dataOutputStream.close(); //close output stream
buffRead.close(); //close buffered Reader
}
}
Using Windows CMD I'm successfully able to send commands, like IRCODE PAUSE to pause the Tivo, but trying to do that here, just gives my COMMAND_TIMEOUT. Here's a sample of the output.
Welcome to Telnet Client
<Telnet Prompt>
Waiting for command:
IRCODE PAUSe
Sending command: IRCODE PAUSe
Response: CH_STATUS 0142 LOCAL
Waiting for command:
IRCODE PAUSE
Sending command: IRCODE PAUSE
Response: COMMAND_TIMEOUT
Waiting for command:
IRCDE PAUSE
Sending command: IRCDE PAUSE
Response: COMMAND_TIMEOUT
Using Windows when I connect, I immediately get the CH_STATUS 0142 LOCAL, so it seems like it's reading the response on a bit of a delay. Here's the guide I followed to get the Windows Telnet working.
Can anyone see why I'm getting the TIMEOUT errors?
According to this forum post, the telnet commands have to be continually issued as it's not expecting delays between commands.
Tivo UI Control via Telnet
I change the method to send commands, and now it seems to work fine. I extracted the code to it's own class.
package tivotelnettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Tivo {
private static final int PORT = 31339;
private Socket pingSocket = null;
private PrintWriter out = null;
private BufferedReader in = null;
public void connect(){
try {
pingSocket = new Socket("192.168.0.10", PORT);
out = new PrintWriter(pingSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(pingSocket.getInputStream()));
} catch (IOException e) {
System.out.println("Error connecting: " + e.getMessage());
}
System.out.println("Connected");
}
public void disconnect() throws IOException {
out.close();
in.close();
pingSocket.close();
}
public void sendCommand(String command) throws IOException {
command = command.toUpperCase().trim();
System.out.println("Sending command: " + command);
out.println(command);
System.out.println("Response: " + in.readLine());
}
}
Obviously it's rather rough at the moment, but it works well. Sending the command .sendCommand("IRCODE GUIDE"); will open up the guide on the Tivo box.
I have a UNIX native executable that requires the arguments to be fed in like this
prog.exe < foo.txt.
foo.txt has two lines:
bar
baz
I am using java.lang.ProcessBuilder to execute this command. Unfortunately, prog.exe will only work using the redirect from a file. Is there some way I can mimic this behavior in Java?
Of course,
ProcessBuilder pb = new ProcessBuilder("prog.exe", "bar", "baz");
does not work.
Thanks!
ProcessBuilder pb = new ProcessBuilder("prog.exe");
Process p = pb.start();
OutputStream pos = p.getOutputStream();
InputStream fis = new FileInputStream("file.txt");
byte[] buffer = new byte[1024];
int read = 0;
while((read = fis.read(buffer)) != -1) {
pos.write(buffer, 0, read);
}
fis.close();
Not tested, but something like this should work.
I ended up doing something like this and it works. Thanks for all the help!
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
public class UserInp {
public static void main(String[] args) {
new UserInp().sample();
}
public void sample() {
String command = "foo.exe";
List<String> args = new LinkedList<String>();
args.add("bar");
args.add("baz");
ProcessBuilder pb = new ProcessBuilder(command);
java.lang.Process process = null;
try {
process = pb.start();
} catch (IOException ex) {
//--
}
OutputStream os = process.getOutputStream();
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
final InputStream is = process.getInputStream();
new Thread(new Runnable() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (java.io.IOException e) {
}
}
}).start();
for (String arg : args) {
pw.println(arg);
}
pw.close();
int returnCode = -1;
try {
returnCode = process.waitFor();
} catch (InterruptedException ex) {
//--
}
System.out.println(returnCode);
}
}
The redirection is setup by the shell you need todo this manually, something like that:
Process proc = pb.start();
OutputStreamWriter os = new OutputStreamWriter(proc.getOutputStream());
// then write the data from your file to os
// ...
os.close();
Did you try to wrap prog.exe into a script which accepts arguments and deal with prog.exe ?
I assume you're using a shell, so it's quite simple to set up a bash script.
If I understand your problem, the script would look like :
#!/usr/bin/bash
echo $1 > file
echo $2 >> file
prog.exe < file
remove file
Build the process using a ProcessBuilder, then use process.getOutputStream() to get an OutputStream that will pipe to the standard input of the process.
Open the file using normal Java techniques, read the contents of the file and write it to the OutputStream going to the Process you made with the ProcessBuilder.
The problem you have right now is that you're calling the ProcessBuilder to launch
$ prog.exe foo bar
Which is probably nothing close to what you want to achieve.