i'm having a problem with what looks like a deadlock.
It's a client/server app. The server has for every socket a thread to read and a thread to write.
Read thread takes client commands, processes it, puts results on a queue, write thread takes it off and writes it out.
Problem is if the read thread is blocking on readLine() and the write thread calls println() it blocks too, and the whole thing hangs. Stacktrace is provided and looks like println() tries to lock a resource that readLine() owns.
Can anyone help?
Simplified example:
ReadThread:
Socket s;
public void run() {
BufferedReader sin = new BufferedReader(
new InputStreamReader(s.getInputStream()));
while (true) {
String line = sin.readLine();
if (line == null) { break; }
String response = "You sent us this: [" + line + "]";
// add response to queue
}
}
WriteThread:
Socket s;
public void run() {
PrintStream sout = new PrintStream(s.getOutputStream(), true);
while (true) {
String toWrite = getFromQueue();
sout.println(toWrite);
removeFromQueue(toWrite);
}
}
The client's code:
public static void main(String[] args) throws IOException {
int portNumber = 51192;
Socket s = new Socket("127.0.0.1", portNumber);
String cmd = "ThisIsATest";
PrintStream out = new PrintStream(s.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));
out.println(cmd);
String result = in.readLine();
s.close();
System.out.println(result);
}
stacktraces: http://pastebin.com/JnsHUFZn
full code of this example: http://pastebin.com/8RcbxgUw
You must have got the server-side Socket from a SocketChannel. The streams associated with such sockets are allocated by the Channels class, and they exhibit the behaviour you describe. The stack traces confirm it.
Use java.net.Socket directly, i.e. via a java.net.ServerSocket in the case of server code. There's no advantage to using SocketChannel or ServerSocketChannel in blocking mode.
Related
We just started studying IO codes and there is a certain point I don't understand this problem :
Here's the server Code :
public final class SuccServer {
public static void main(String[] args) {
try (ServerSocket s0 = new ServerSocket(5108);
Socket s = s0.accept();
BufferedReader r =
new BufferedReader(
new InputStreamReader(s.getInputStream(),
US_ASCII));
BufferedWriter w =
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream(),
US_ASCII))) {
int i = Integer.parseInt(r.readLine());
int i1 = i + 1;
w.write(String.valueOf(i1));
w.write('\n');
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
And here's the Client code :
public final class SuccClient {
public static void main(String[] args) {
try (Socket s = new Socket("localhost", 5108);
BufferedReader r =
new BufferedReader(
new InputStreamReader(s.getInputStream(),
US_ASCII));
BufferedWriter w =
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream(),
US_ASCII))) {
int i = 2019;
w.write(String.valueOf(i));
w.write('\n');
w.flush();
int succ = Integer.parseInt(r.readLine());
System.out.printf("succ(%d) = %d%n", i, succ);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
First the connection is established so the accept() creats the socket, then I don't understand how the code will work(chronologically), why is the client's write instruction
w.write(String.valueOf(i));
that is executed first and not the server's
int i = Integer.parseInt(r.readLine());
, and why after writing 2019 does the client wait for the server's response ? he could simply continue the code by executing
int succ = Integer.parseInt(r.readLine());
without waiting for the server to respond him with 2020 ?
These questions could seem simple but they don't let me understand more difficult code.
Simply put it's because readLine() and accept() are blocking.
"In computing, a process is an instance of a computer program that is being executed. A process always exists in exactly one process state. A process that is blocked is one that is waiting for some event, such as a resource becoming available or the completion of an I/O operation"
Program would hang at readline() until newline or end of stream is reached, just like when accept() is called on the server it hangs until a client connects.
edit: Here is another explanation with focus on network sockets.
UPDATE:
I noticed that it works fine on a Windows machine, but it fails on Mac.
I created a basic Java Server code for socket connection. It runs on AWS Linux AMI, and I make a client connection from my computer. It works fine for first 4-5 answers from the client. However, after 4th or 5th answer, I don't get any response from the server and it just hangs. After a while, it gives SocketTimeout Exception.
I set socket timeout with setSoTimeout() in both side, and it didn't change anything. I wonder if it has anything to do with Amazon.
Client Code:
public static void main(String[] args) {
final int portNumber = 9090;
String connected = "1";
System.out.println(WELCOME);
try {
Socket socket = new Socket("ip", portNumber);
socket.setSoTimeout(0);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader input = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
out.println(connected);
while (connected.equals("1")) {
//read in situation
String situation = readInSituation(socket, input).replace(DELIMETER, "\n");
System.out.println(situation);
//send option
Scanner in = new Scanner(System.in);
System.out.print("Enter option: ");
out.println(in.nextLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String readInSituation(Socket socket, BufferedReader input) throws Exception {
while (!input.ready()) {
Thread.sleep(200);
}
return input.readLine();
}
Server Code is more complicated, but all it does is to respond to commands and print basic text on the screen.
Thanks
Don't use PrintWriter over the network, as it swallows exceptions, most probably a prior 'connection reset'. Use BufferedWriter. NB The ready() loop is literally a waste of time.
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();
}
}
}
(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.
How does one set a timeout on a BufferedReader and a PrintWriter created using a socket connection? Here is the code I have for the server right now, which works until either the server or the client crashes:
while(isReceiving){
str = null;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
while ((str = br.readLine()) != null){
System.out.println("Processing command " + str);
pw.println(client.message(str));
}
}
Outside the scope of this code I have imposed a socket timeout of 1000ms, which works as intended when waiting for the initial connection. But the program blocks at (str = br.readLine()). If the client hangs or crashes, it never stops blocking unless I terminate the process (which even then doesn't always work).
The client code in question is very similar to this, and is blocking in a similar fashion.
You need to set a read timeout on the socket, with Socket.setSoTimeout(). This will cause any read method to throw a SocketTimeoutException if the read timeout specified expires. NB Read timeouts are set not on the stream but on the underlying Socket, via Socket.setSoTimeout().
There is no such thing as a write timeout in TCP.
You could use SimpleTimeLimiter from Google's Guava library.
Sample code (in Java 8):
BufferedReader br = ...;
TimeLimiter timeLimiter = new SimpleTimeLimiter();
try {
String line = timeLimiter.callWithTimeout(br::readLine, 10, TimeUnit.SECONDS);
} catch (TimeoutException | UncheckedTimeoutException e) {
// timed out
} catch (Exception e) {
// something bad happened while reading the line
}
An answer in this question describes an interesting method using a Timer to close the connection. I'm not 100% sure if this works in the middle of a read, but it's worth a shot.
Copied from that answer:
TimerTask ft = new TimerTask(){
public void run(){
if (!isFinished){
socket.close();
}
}
};
(new Timer()).schedule(ft, timeout);
isFinished should be a boolean variable that should be set to true when you're done reading from the stream.
Since calling socket.close() did not seem to interrupt the block at br.readLine(), I did a little workaround. When disconnecting the client from the server, I merely send through a string "bye", and told the server to close the socket connection when it receives this command.
while ((str = br.readLine()) != null){
// If we receive a command of "bye" the RemoteControl is instructing
// the RemoteReceiver to close the connection.
if (str.equalsIgnoreCase("bye")){
socket.close();
break;
}
System.out.println("Processing command " + str);
pw.println(client.message(str));
}
I have Client class and Server class but when i run both main methods and then nothing will happen and when i stop running ,this exception will be occurred. why?? please help me,how can I fix it???
my Client class:
public class Client {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
Socket c = new Socket("localhost", 5001);
BufferedReader read = new BufferedReader(new InputStreamReader(c.getInputStream()));
BufferedWriter write = new BufferedWriter(new OutputStreamWriter(c.getOutputStream()));
String string = reader.readLine();
write.write(string, 0, string.length());
write.newLine();
write.flush();
System.out.println(read.readLine());
} catch (Exception e) {
System.err.println(e);
}
}}
my Server class:
public class Server{
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
ServerSocket s = null;
try {
s = new ServerSocket(5001);
System.out.println("listening...");
Socket so = s.accept();
BufferedReader read = new BufferedReader(new InputStreamReader(so.getInputStream()));
BufferedWriter write = new BufferedWriter(new OutputStreamWriter(so.getOutputStream()));
while (true) {
String string = read.readLine();
System.out.println(string);
String answer = "I got" + string + "from you!";
write.write(answer, 0, answer.length());
write.newLine();
write.flush();
}
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}}
stacktrace in server cpnsole:
run:
listening...
system connected
Hello
Dec 19, 2009 12:58:15 PM server.Main main
SEVERE: null
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:168)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.fill(BufferedReader.java:136)
at java.io.BufferedReader.readLine(BufferedReader.java:299)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at server.Main.main(Main.java:37)
BUILD SUCCESSFUL (total time: 9 seconds)
in Client console:
run:
Hello
I gotHellofrom you!
BUILD SUCCESSFUL (total time: 4 seconds)
Your client connects to the server, sends some data, reads the response and terminates. That's ok.
But your server waits for a client, reads its data, writes a response and then tries to read some data from the client again. But the client has closed the connection. So the server gets the exception you described.
To fix this (on server side), you have to do the Socket so = s.accept(); within your while loop. And don't forget to close the socket at the end of the loop.
First. A BufferedWriter isn't useful with Sockets. Use a PrintWriter witch flushes automatically.
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);// true means "flush automatically"
writer.println(reader.readLine());
// Now you don't have to add a newline and flush.
Edit
Secondly. The reason why the exception is thrown is because the client closes the Socket after writing your input to the Server. The server is still waiting for another String to read. But he can't because the Socket is closed. You don't close it literally. But the program ends there. So Java think: "Nothing left to do, exit". By exiting, the connection closes.
To solve it you have to put the communication in a while(true) loop and, to stop the connection in a correct way, send an "end-of-connection" message.
Client side:
while (true)
{
String userinput = reader.readLine(); // From System.in
writer.writeln(userinput);
if (userinput.equals("end"))
{
socket.close();
break; // break out of the while(true) loop
}
}
Server side:
while (true) {
String socketinput = reader.readLine();
if (socketinput.equals("end"))
{
socket.close();
break; // Break out of the while(true) loop.
}
... // Handle the socketInput
}
That is also what "Connection reset" means.
Martijn.