When I receive data using Socket.getInputStream()
directly (without some kind of interface like Scanner), it doesn't block. But, when I try to use a Scanner (similar to how we receive Strings from System.in
), it does. I was wondering the reason for this, and how the InputStream that a connected Socket supplies to you is different from the InputStream in
in System
.
The Client used for testing (used for both servers)
The code that hangs:
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(15180);
Socket socket = ss.accept();
Scanner scanner = new Scanner(socket.getInputStream());
//read data from client
while(true) {
String data = scanner.nextLine();
System.out.println("Received data!");
}
}catch(IOException e) {
e.printStackTrace();
}
}
}
The code that doesn't block:
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(15180);
Socket socket = ss.accept();
//read data from client
while(true) {
int data = socket.getInputStream().read();
System.out.println("Received data!");
}
}catch(IOException e) {
e.printStackTrace();
}
}
}
However, if you construct a Scanner using an InputStream that may require waiting for data to arrive (e.g., reading text from a remote client, waiting for a page to load so you can read its source, etc.) then Scanner#nextLine() will block because it needs to wait for enough information to build the next line to arrive.
That's because the Scanner. nextInt method does not read the newline character in your input created by hitting "Enter," and so the call to Scanner. nextLine returns after reading that newline. You will encounter the similar behaviour when you use Scanner.
The nextLine() method of the java. util. Scanner class scans from the current position until it finds a line separator delimiter. The method returns the String from the current position to the end of the line.
nextLine() method advances this scanner past the current line and returns the input that was skipped. This method returns the rest of the current line, excluding any line separator at the end. The position is set to the beginning of the next line.
(I think you've already figured this out but ...)
The readLine()
method returns the rest of the current line. That is, all unconsumed characters up to the next "end of line" sequence, or the "end of stream", which ever comes first. It will block, waiting until the current line (according to the above) is available.
So if your socket readLine()
call blocks, it is waiting for the remote to either send an end-of-line marker (e.g. '\n'
), or close its socket output stream (which will result in an "end-of-stream" at this end).
Q: Why does it "work" when you read from the console?
A: The console adds an "end-of-line" sequence to the stream whenever you hit ENTER. (Precisely what sequence is added is OS dependent, but the Scanner
class will cope with all common varieties, and some unusual ones too.)
The lesson here is that you should only use Scanner.readLine()
if the input stream is line oriented; i.e. if whatever wrote / generated the stream is including "end-of-line" markers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With