I got this question from Head First Java by Kathy Sierra and Bert Bates. In the Networking and Threads section of the book, they built a chat client and handled incoming messages by starting a separate thread:
public class IncomingReader implements Runnable {
public void run() {
String message;
try {
while ((message = reader.readLine()) != null) { //reader is a BufferedReader from an InputStreamReader of a Socket
System.out.println("read " + message);
incoming.append(message + "\n"); //incoming is a JTextArea they declared earlier
}
} catch (Exception ex) {ex.printStackTrace();}
}}
And this thread is started only once, after they setup the Swing GUI and the readers and writers.
So my question is, how is this thread able to stay alive and keep listening for incoming messages. Shouldn't it go past the while loop and die when message is null?
BufferedReader will keep on reading the input until it reaches the end. But if there is nothing to read, then it will keep looping or wait until there is input.
BufferedReader readLine() blocks
Related
I'm making a simple chat program using sockets, and I have an ArrayList to store active objected classes with sockets and usernames for each connected client, and inside those classes it checks if the socket is still open, and calls for its self to be joined if it is not.
Server file kick code:
public synchronized void kick(ClientThread client){
try{
output.printLine("kicking " + client.getUsername());
clients.remove(client);
output.printLine("removed " + client.getUsername());
}catch(Exception e){
e.printStackTrace();
}
output.printLine("kicked ");
}
This is what is called by the client object if its socket is closed.
clients being the ArrayList which stores the clients
client being the class object which I want to join, the client object extends Thread
public void run(){
while(true){
try{
Thread.sleep(10);
if(isValid()){
/*bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if(bufferedReader.ready()){
lastMsg = bufferedReader.readLine();
sendMessage("Hello");
bufferedReader.close();
}*/
}else{
output.printLine(this.getUsername() + " connection closed");
break;
}
}catch(Exception e){
e.printStackTrace();
}
}
server.kick(this);
try {
join();
} catch (InterruptedException e) {
e.printStackTrace();
}
output.printLine("joined " + getUsername());
}
This is the run function from the client object, at the bottom is the code which is run when the socket is not connected, it breaks from the loop and should join its self, however it hangs on joining its self. The client is removed from the ArrayList properly but no matter what i do i cannot get the thread to join.
This cannot work. To join a thread means waiting until it died. If you wait for yourself to die, you will wait forever, because as long as you wait you cannot die.
if the socket is still open, and calls for its self to be joined if it is not.
A thread cannot call for itself to be joined. If a thread wants to quit then it just returns from the run() method. Other threads that are waiting for that thread to die can call thread.join() on it. But as #Vampire pointed out, for a thread to call this.join() will mean that it is waiting for itself to finish but it never will because it is waiting for itself to finish. Joinception indeed.
but no matter what i do i cannot get the thread to join.
I think the question here is what are you trying to achieve. If you want the thread to finish then just return from the run() method. If you are trying to notify other threads that you are finishing then they should do the join-ing.
I wanna kill the TCP connection listener thread(serverside) after client closes the socket..
The thread waits in the loop in the readLine()..
How can i do it?
while(isconnected){
String msg = in.readLine();
//..
}
You have to call socket.close() method, if you are using it properly it should be fine. I don't know where readLine() is coming from, so I will assume its BufferedReader. If you look here in the documentation BufferedReader readLine()
you will see that it throws IOException if there is an error and if it is end of stream it will return null.
so you should basically do this:
try{
while(socket.isConnected()){
String line = in.readLine();
if(line==null){
//END OF STREAM
}
}
}catch(IOException e){
//deal with IOException here
}
otherwise, what I assume your currently doing is sitting in a tight loop as soon as the other end disconnects. If you try too print out msg in your above code you will see it print out null nonstop.
Perhaps extend your protocol so that the client sends a QUIT message before closing its socket.
First, you can't tell if the client is just taking a long time to respond, or if it is down.
What you can do is set some timeout period and have a thread in the server that calls clientSocket.close() after the timeout has elapsed. This will throw a SocketException in the receiving thread. It will take you out of the receiving loop and the thread will just terminate by itself if there is nothing after the receiving loop.
WalterM is basically right. The readLine call will return null is the stream is closed by the remote client, and will throw an exception if the connection "breaks" without a proper close, or the low-level socket read times out.
It is worth pointing out that it is simpler and more efficient to just do this:
try {
String msg;
while ((msg = in.readLine()) != null) {
// do stuff
}
} catch (IOException ex)
// report error
} finally {
// Close the socket under all circumstances to avoid potential
// resource leakage
try {
socket.close();
} catch (IOException ex) {
// ignore
}
}
Checking that the socket is still connected it redundant. The low-level socket read will be doing that anyway.
You'll need to interrupt the thread.
I have written a server is java here is the code:
public mainClass()
{
try
{
ss = new ServerSocket(8080);
while (true)
{
socket = ss.accept();
System.out.println("It is accept!");
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//out = new PrintWriter(socket.getOutputStream(),true);
line = in.readLine();
System.out.println("you input is :" + line);
//out.close();
in.close();
socket.close();
}
}
catch (IOException e)
{
}
and I am using an iPhone application as the client.
now what my problem is that the server is not reading the inputstream while the appication is running on the iphone.. But as soon as the application is terminated the java program prints out the String which has been sent to the server..Not sure what is happening here..sorry if this is not a good question..
- (void)viewDidLoad {
[super viewDidLoad];
socket = [[LXSocket alloc]init];
if ([socket connect:#"10.211.55.2" port:8080]) {
NSLog(#"socket has been created");
}
else {
NSLog(#"socket couldn't be created created");
}
#try {
[socket sendString:#"Hi This is a second test"];
}
#catch (NSException * e) {
NSLog(#"Unable to send data");
}
[super viewDidLoad];
}
thanks,
TC
From my own experience, readLine is not a good idea, especially when working with different languages and platforms, a better approach will be to use InputStreamReader and its read(char[] buff) method, and agree on both sides regarding the length to be sent each time.
Again, I have no reference to that, only my experience.
Also, looking at your code, you send a string without a new line character: [socket sendString:#"Hi This is a second test"]; maybe adding \n at the end will solve it for you.
My guess is that the client application doesn't send any line break at the end of the string it sends. So BufferedReader.readLine() waits for an EOL character, and only returns the string when the client application ends, because at this point the connection is closed and the reader knows there won't ever be an EOL, and the string is the last line it will ever receive.
BufferedReader can be dangerous; the buffering can cause short lines to get "stuck" if you're only reading a little data at a time, or if the data is coming across a network. If you're only using BufferedReader to get readLine(), then do this:
new BufferedReader(new InputStreamReader(socket.getInputStream()), 1);
That extra argument sets the buffer size to 1 character, effectively turning it off. That generally solves this kind of problem.
I have socket already declared socket like this:
serverAddr = InetAddress.getByName(this.ip);
socket = new Socket(serverAddr, port);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
however, the following doesn't work. in.ready() always returns false and if removed the program will freeze at String message = in.readLine();
private void receive() {
try {
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
System.out.println(isr.getEncoding());
BufferedReader in = new BufferedReader(isr);
if (in.ready()) {
String message = in.readLine();
if (message != null) {
if (listener != null) {
listener.receiveMessage(ip, message);
} else {
print("Client recieved: " + message);//
}
}
}
in.close();
} catch (Exception e) {
print("Error with input stream: " + e);
disconnect();
}
}
How could i solve this?
EDIT:
This is how sending looks like in my server class:
out.println(message);
out.flush();
This happens in a loop whenever i've put something in message. out is closed after this loop.
You shouldn't be using ready() like this. The javadoc says this:
"Returns: True if the next read() is guaranteed not to block for input, false otherwise. Note that returning false does not guarantee that the next read will block. "
Your code is implicitly assuming that ready() -> false means that the next read will block. In actual fact, it means the next read might or might not block.
As #EJP says ... just do the read call.
What could i do to prevent a block though? The client will be unable to send anything if it's blocked
If blocking in read is a problem for your application, either use a separate thread to do the reading, or change your code to use NIO channel selectors.
Just remove the in.ready() test. It isn't helping you. readLine() will block until there is data available. What else were you planning to do if no data has arrived yet?
There are 3 things that come to my mind:
You are re-opening the input stream in every receive call, and wrapping it into a BufferedReader. This might read more than a single line into the buffer, and after finishing (closing it), the remaining buffered bytes will no longer be available for subsequent receive calls
Did you think about using an own thread for reading the server messages? There it won't harm if it is blocked
I have experienced some problems when closing one side of a socket after writing data, and immediately closing it. Sometimes not all of the data was received by the other side, despite flush() and close() calls. Maybe this is also an issue in your situation
Edit:
Smiply keeping the in reference outside of the receive method will not fully solve your problem. You should use a while loop for reading all buffered messages and call the listener for everyone, e.g.:
if (in.ready()) {
String message;
while ((message = in.readLine()) != null) {
// ...
}
}
But watch out as the last line might be a partially read message (e.g. 3 and 1/2 messages were buffered). If this is an issue, you could read the messages char-by-char for determining when a line ends, and use a PushbackReader for putting back incomplete messages.
You may need to call out.flush() to flush anything in BufferedWriter
I am trying to read input from a socket line by line in multiple threads. How can I interrupt readLine() so that I can gracefully stop the thread that it's blocking?
EDIT (bounty): Can this be done without closing the socket?
Without closing the socket:
The difficult problem isn't the BufferedReader.readLine, but the underlying read. If a thread is blocked reading, the only way to get it going is to supply some actual data or close the socket (interrupting the thread probably should work, but in practice does not).
So the obvious solution is to have two threads. One that reads the raw data, and will remain blocked. The second, will be the thread calling readLine. Pipe data from the first the second. You then have access to a lock than can be used to wakeup the second thread, and have it take appropriate action.
There are variations. You could have the first thread using NIO, with a single thread instance shared between all consumers.
Alternatively you could write a readLine that works with NIO. This could even take a a relatively simple single-threaded form, as Selector.wakeup exists and works.
Close the socket on the interrupting thread. This will cause an exception to be thrown on the interrupted thread.
For more information on this and other concurrency issues, I highly recommend Brian Goetz's book "Java Concurrency in Practice".
Sorry for being over 6 years late ;-) I had a need for some interruptible readLine when reading from the keyboard, for a simple hobby console application. In other words, I couldn't "close the socket".
As you may know, System.in is an InputStream that apparently already does some buffering (you need to press Enter]). However, it seems to be suggested to wrap it in a BufferedReader for better efficiency, so my input is from:
BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in));
The other thing one might have discovered is that BufferedReader.readLine() blocks until input is provided (even if the thread is interrupted, which seems to only end the thread once readline() gets its input). It is however possible to predict when BufferedReader.read() will not block, by calling BufferedReader.ready() == true. (However, == false does not guarantee a block, so beware.)
So I have incorporated the above ideas into a method that reads the BufferedReader character by character, checking in between each character if the thread has been interrupted, and also checks for end-of-line, at which point the line of text is returned.
You may find this code useful, pass the consoleIn variable as declared above. (Criticism may be welcomed too...):
private String interruptibleReadLine(BufferedReader reader)
throws InterruptedException, IOException {
Pattern line = Pattern.compile("^(.*)\\R");
Matcher matcher;
boolean interrupted = false;
StringBuilder result = new StringBuilder();
int chr = -1;
do {
if (reader.ready()) chr = reader.read();
if (chr > -1) result.append((char) chr);
matcher = line.matcher(result.toString());
interrupted = Thread.interrupted(); // resets flag, call only once
} while (!interrupted && !matcher.matches());
if (interrupted) throw new InterruptedException();
return (matcher.matches() ? matcher.group(1) : "");
}
... And in the thread that is calling this, catch the exceptions and end the thread appropriately.
This was tested in Java 8 on Linux.
I was playing around with this recently (using Scala), and I didn't like the accepted answer of closing the socket and getting an exception.
Eventually I discovered that it's possible to call socket.shutdownInput() in the interrupting thread to get out of the readLine call without an exception. I make this call in a SIGINT handler so that I can clean up and close the socket in the main thread.
Note, that the equivalent exists for the outputstream with socket.shutdownOutput()
you can design a Timer class around the read() block.
you need to set a timeout for your timer.
on timeout just interrupt your thread.
Without closing the socket, no question the best solution with the least overhead is to simply avoid using the blocking read methods until the BufferedReader is ready, or a timeout is reached.
public String readLineTimeout(BufferedReader reader, long timeout) throws TimeoutException, IOException {
long start = System.currentTimeMillis();
while (!reader.ready()) {
if (System.currentTimeMillis() - start >= timeout)
throw new TimeoutException();
// optional delay between polling
try { Thread.sleep(50); } catch (Exception ignore) {}
}
return reader.readLine(); // won't block since reader is ready
}
If you want to use readLine on a server socket within a client-server tcp architecture, for instance, you can use setSoTimeout(int timeout) of java.net.Socket.
From the Socket#setSoTimeout(int timeout) Documentation:
Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid.
public class MainApp {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(11370);
Socket clientSocket = serverSocket.accept();
clientSocket.setSoTimeout(2000);
executorService.execute(new ReadingThread(clientSocket));
// ... some async operations
executorService.shutdown();
}
}
public class ReadingThread implements Runnable {
private final Socket clientSocket;
public ReadingThread(Socket clientSocket) {
this.clientSocket = clientSocket;
}
#Override
public void run() {
BufferedReader socketReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String readInput = null;
while (!Thread.currentThread().isInterrupted()) {
try {
readInput = socketReader.readLine();
} catch (SocketTimeoutException e) {
continue;
}
}
// operations with readInput
}
}
The main application implements a server socket which listens to connections and has a thread pool. If an incoming client communication is accepted, then a new Thread from the pool is assigned and the run function is invoked in ReadingThread (can be adjusted to allow multiple threads).
On the socket used for communicating to the client the property setSoTimeout(int timeout) has been set. Therefore if readLine does not return within the specified timeout a SocketTimeoutException is thrown.
You can check in a loop whether the ReadingThread has been interrupted by the main application, and if so stop reading from the socket.
When the buffered reader is being used to read the input stream from a socket then you can achieve this by having the read call timeout. Once this timeout is triggered you will be able to check if your thread should be stopped. To do this call setSoTimeout on the socket. The read call will then have a SocketTimeoutException and you can use that to stop the thread.
#Override
public void run() {
running = true;
try {
socket.setSoTimeout(1000); // This will determine how quick your thread responds to the shutdown call
var inputStream = socket.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
} catch (IOException e) {
Logger.error("IOException while setting up input stream");
Logger.error(e);
return;
}
StringBuilder stringBuilder = null;
while (running) {
try {
int singleChar = bufferedReader.read();
// Do something with the data
} catch (SocketTimeoutException e) {
// SocketTimeoutException is expected periodically as we do setSoTimeout on the socket,
// this makes the above read call not block for ever and allows the loop to be interrupted
// cleanly when we want to shut the thread down.
Logger.trace("Socket timeout exception");
Logger.trace(e);
} catch (IOException e) {
Logger.error("IOException while reading from socket stream");
Logger.error(e);
return;
}
}
}
public void stopThread() {
running = false;
try {
bufferedReader.close();
} catch (IOException e) {
Logger.error("IOException while closing BufferedReader in SocketThread");
Logger.error(e);
}
}
Answer found here: Any way of using java.nio.* to interrupt a InputStream#read() without closing socket?
I think that you might have to use something other than readLine(). You could use read() and at every loop iteration check to see if the thread was interrupted and break out of the loop if it was.
BufferedReader reader = //...
int c;
while ((c = reader.read()) != -1){
if (Thread.isInterrupted()){
break;
}
if (c == '\n'){
//newline
}
//...
}
A sketch for a solution might be this: NIO provides methods for nonblocking IO, so you have to implement something called Foo that uses nonblocking NIO on the socket end but also provides a InputStream or Reader interface on the other end. If the BufferedReader enters its own read, it will call Foo, which will call Selector.select with read intent. select will either return indicating the presence of more data or it will block until more data is available.
If another thread wants to unblock the reader, it must call Selector.wakeup and the selector can return gracefully by throwing an exception the by BufferedReader.
The socket should be still open after that.
Variation A: call Selector.select(timeout) to do busy polling light.