Is BufferedReader blocking the standard console input? - java

I'm writing a simple chat application in Java and everything works fine except the following strange bug:
When you type something in the terminal, for example you press "a" and the console writes "a", then you press "i", it writes "i", "r" -> "r"... and you get the word (e.g. "air") written in console.
In my case I press "e", the console writes "(nothing)", "i" ->"(noting)", "r" -> (nothing) and only after I pressed "enter" console suddenly displays the whole word "air", works in the same way with more than one words. In reality the user can write whatever he/she wants but the typed down text will appear only after Enter has been pressed.
In my program I use a BufferedReader object to read from the socket:
try( ....some socket stuff....
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);//out is the socket output
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); //in is the socket input
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); //that's the standard input, the one from keyboard
while(true){
if (stdIn.ready()){
userInput=stdIn.readLine();
out.println(userInput);
if(userInput.equals("Bye.")) break;
System.out.println("Me: "+userInput);
}
if(in.ready()){
if((serverInput= in.readLine())!=null){
System.out.println("Stranger: "+serverInput);
}
}
In my opinion that methods - ready and readline() - probably bug the console. It seems a little bit weird for me but it's Java and I'm not very skilled.
Apart from that the chat works like charm - it sends and receives properly. The read-receive-input script is exactly the same and on the client side. When you're typing a message nothing will show up in console but it actually is there and on Enter it displays, it's not a delay, it's influenced by Enter.

This is not possible to do in java.
At least not with platform independent way.
What you are trying to do is, read character by character, without pressing enter key.
But the console on os itself by default is in buffered line mode.
So, only solution is to put your console on raw mode (line editing bypassed and no enter key required), making it platform dependent.
Edit : Your description says character reading, and your code is doing something else by the way.

Related

Reading System.in character by character without buffering

I know that similar questions have been asked before, but not exactly what I'm asking. To begin with, let me explain my purpose. I'm trying to write a kind of "remote shell" that will take in characters from the console (System.in) on character at a time and then send those to a remote session on another machine, write them to that machine and gather any characters it might output to return to my shell to display back to the user.
So, the issue is that System.in, no matter what I do, doesn't really support a "raw" mode where any type of reader is able to read just one character at a time UNTIL a terminator character is entered, typically new line.
Things I have tried, Using Scanner, using a buffered reader, creating a FileDescriptor.in and creating a fileInputStream from that, using a FileChannel and reading into a ByteBuffer that is one character long, etc. In all cases, it seems, System.in only makes characters available to the java application after a terminator character has been entered by the user. I'm convinced there is not a "java" way to do this, so the question is, does anyone have some native code, wrapped in a java library to do this? Its hard to find such a thing just searching GitHub.
BTW, for the remote console, I'm using the pty4J package. I've seen sample projects that connect to that code using other langauages, for example javaScript running in a browser to create a web based shell. Other languages all you to do a simple "get_char" on standard in.
Some examples of the code I've tried:
Scanner scanner = new Scanner(System.in);
FileDescriptor fd = FileDescriptor.in;
FileInputStream fis = new FileInputStream(fd);
FileChannel fc = fis.getChannel();
while(process.isAlive()) {
System.out.println(scanner.next());
// ByteBuffer bb = ByteBuffer.allocate(1);
// int c = fc.read(bb);
// int c = fis.read();
// System.err.println("Read " + c);
// if (c == 1) {
// os.write(bb.get());
// }
}
You can see that I've tried various methods to read the input: scanner.next(), fc.read(byteBuffer), fileInputStream.read(), etc. All attempts "wait" till a terminator character is entered.
Additionally, I have tried using the "useDelimiter" and "next(pattern)" methods on the scanner too. That's still not working.
Any pointer or help is much appreciated.
Below is an example of reading one character at a time until end of stream is reached. On linux, you type control-d to signal the end input. I think on Windows, you type control-c to end of input.
import java.io.*;
class Test {
public static void main(String[] args) throws IOException {
int c = 0;
while( (c=System.in.read()) != -1){
System.out.println((char) c);
}
}
}

Java encoding from OS X to Windows

I have made a primitive multi-client chat with swing GUI. Everything works fine as long as both people write from the same OS. If one of them writes from Windows and the other from OS X, the encoding of some special characters goes nuts. ( I am from CZE, we use characters as š,ě,č,ř,ž...). I have searched for a long time but didn't find anything that would help.
I have input and output defined as:
in = new BufferedReader(new InputStreamReader(soc.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(soc.getOutputStream()));
where soc is the socket used for connecting to the server side.
The sending process is as simple as:
out.println(message);
where message is a String, which I got from JTextArea by calling method .getText()
I know why this problem occurs, but I was unable to find any reasonable solution.
Any help will be appreciated.
Thanks
When reading character data from Input/OutputStreams, it's a good practice to always specify the character encoding. Otherwise the platform default encoding is used (which might not be the same on all systems).
in = new BufferedReader(new InputStreamReader(soc.getInputStream(), StandardCharsets.UTF_8));
out = new PrintWriter(new OutputStreamWriter(soc.getOutputStream(), StandardCharsets.UTF_8));

Flush/Clear System.in (stdin) before reading

At work, we have 5 RFID readers attached to a PC running Linux. The readers are all recognized as keyboards and send their input (what they read form the Chip) as an key-input-event sequence. To be able to tell which reader send what sequence, I'm doing a raw-read over /dev/input/XX and get their input this way.
The problem with this is, that the send keyboard-events generated by the RFID readers are still "in" stdin and when I try to read from System.in via Scanner (input should be generated by a normal keyboard this time), I first get the "pending" input from the readers (which consists of 10 Hex-decimal digits and a newline (\n)).
Now, the question is: How can I flush all these "pending" input's from stdin and then read what I really want from the keyboard?
I tried:
System.in.skip(System.in.available());
But seek is not allowed on stdin (skip throws an IOException).
for (int i = 0; i < System.in.available(); i++){
System.in.read();
}
But available() doesn't estimate enough (still stuff in stdin afterwards).
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
scanner.nextLine();
}
System.out.println("Clean!");
But hasNextLine() never becomes false (the print never executes).
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = in.readLine()) != null);
System.out.println("Clean!");
Same as above.
Anyone with any more ideas?
Based on #Joni's advice, i put this together:
Scanner scanner = new Scanner(System.in);
int choice = 0;
while (scanner.hasNext()){
if (scanner.hasNextInt()){
choice = scanner.nextInt();
break;
} else {
scanner.next(); // Just discard this, not interested...
}
}
This discards the data that is already "pending" in stdin and waits until valid data is entered. Valid, in this context, meaning a decimal integer.
This worked for me
System.in.read(new byte[System.in.available()])
A related one.
I read a double, then needed to read a string.
Below worked correctly:
double d = scanner.nextDouble();
scanner.nextLine(); // consumes \n after the above number(int,double,etc.)
String s = scanner.nextLine();
There is no built-in portable way to flush the data in an input stream. If you know that the pending data ends with \n why don't you read until you find it?
Devices usually send data using a well defined protocol which you can use to parse data segments.
If I'm right, discard data that isn't properly formatted for the protocol. This allows you to filter out the data you aren't interested in.
As I'm not familiar with the RFID scanner you're using I can't be of more help, but this is what I suggest.
You could do this with multiple threads.
Your real application reads from a PipedInputStream that is connected to a PipedOutputStream
You need to have one thread reading from System.in continuously. As long as the real application is not interested in the data coming from System.in (indicated by a boolean flag), this thread discards everything that it reads. But when the real application sets the flag to indicate that it is interested in the data coming from System.in, then this thread sends all the data that it reads to the PipedOutputStream.
Your real application turns on the flag to indicate that it is interested in the data, and clears the flag when it is no longer interested in the data.
This way, the data from System.in is always automatically flushed/clead
The best practice (that I've found) when dealing with terminals (aka. the console) is to deal with i/o a line at a time. So the ideal thing to do is get the entire line of user input, as a string, and then parse it as you see fit. Anything else is not only implementation specific, but also prone to blocking.
Scanner sc = new Scanner(System.in);
String line = "";
while (true) {
System.out.println("Enter something...");
try {
line = sc.nextLine();
//Parse `line` string as you see fit here...
break;
} catch (Exception e) {}
}
I include the while & try/catch blocks so that the prompt will loop infinitely on invalid input.
You can try:
System.in.skipNBytes(System.in.available());

Is BufferedReader ignoring the first line?

I am currently writing a service that should take cleartext commands and then return something according to thoose commands, which is also in cleartext.
I have this odd problem with BufferedReader, or, it might be telnet that is odd, for some reason the BufferedReader reads the first command, however that command is ignored no matter what i do, which i can get around by sending the first command twice, but that is just stretching it a bit, in my oppinion.
The code below is in a run() method.
Then i set out as a PrintWriter and in as a BufferedReader.
The runs variable is by default true.
out = new PrintWriter(handle.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(handle.getInputStream()));
while (runs) {
String msg;
msg = in.readLine();
String[] command;
command = msg.split(" ", 3);
/* do something with the command */
}
So my question is if BufferedReader is ignoring the first line or is it telnet that is not coorperating with me?
If it is something else, then please enlighten me.
EDIT
I got this debug message:
Debug: ���� ����'������/nick halmark
so i suppose that it is about all the questionmarks.
I am actually using the latest Putty since i am developing on a windows box... and as far as i recall... then it does not exist by default
If you are using PuTTY, you need to choose the "Raw" Connection Type.
Microsoft telnet servers like to have some content/protocol negotiation at the beginning, so PuTTY will do this by default as per the RFC 854 spec. That's the garbage that you are reading.

Java Receiving Objects over Sockets

I'm developing a hangman game for college. And it need to be over sockets, with server and clients. The game works fine by now.
But when its played by two people, trying to guess the same word, i need to know what letter one user choose, so i can disable that letter on the other user GUI.
So, i'm trying to send an letter object with an id that will disable the button on the other side, i'm sendind a message first, warning that i will send an object, so i don't get an exception.
Bur its only reading the first two objects that i send, on the third one i get an exception called java.io.StreamCorruptedException. Anyone knows why?
Sending:
toClient= new DataOutputStream(socketConection.getOutputStream());
toClient.writeBytes("VK_Letra\n");
objectToClient= new ObjectOutputStream(socketConetion.getOutputStream());
objectToClient.writeObject(new Letter());
objectToClient.flush();
Receiving:
fromServer = new BufferedReader(new InputStreamReader(socketCliente.getInputStream()));
objectFromServer = new ObjectInputStream(socketCliente.getInputStream());
public void run(){
String str;
try{
while(true){
str = fromServer.readLine();
if(str.equals("VK_Letter")){
Letter l = (Letter) objectFromServer.readObject();
System.out.println(l.getLetter());
button[l.getId()].setEnabled(false);
}else{
chat.append(str+ "\n");
chat.setCaretPosition(chat.getDocument().getLength());
}
}
}catch(IOException | ClassNotFoundException e){
JOptionPane.showMessageDialog(/* Error Message */);
}
}
The approach that I would use would be to have everything as an object so that you are only sending and receiving objects. Then the object would indicate the actual message along with any parameters or arguments or data.
You are mixing up between a read line and getting objects.
A common source of confusion is when you mix different stream types on the same stream.
You have a fine example of two different problems.
You should not use multiple streams on the same stream e.g. DataOutptuStream and ObjectOutputStream
You should not mix binary and text streams such as BufferedReader and ObjectInputStream.
The solution is to use one, and only one, which suits your needs and use that.

Categories