Question

I'm making a Java (version controll) application where a client and server communicate through socket streams. The problem that I have is that the client or server often tend to block when they do a readline(), eventually causing the other side to block as well. Oddly enough, the error seems not to occur when ran for the first time.

I have spend a lot of time in figuring out what causes the problem and how to solve it, but I have only been able to strongly reduce my problem to the code provided below. The MAKE_STUB parameter allows to use piped streams instead of socket streams. It often leads to an error caused when the thread is closed before the client has finished reading, but sometimes the same error as when using sockets also occurs, which presumably indicates that the problem is not caused by misuse of sockets, but rather misuse of streams?

I have furthermore tried the following things: changing when the output is flushed, adding + "\n" in println(), sending/receiving in turn (PING_PONG), preventing the streams from being garbage collected (using a static list), sending a string instead of the JSON, etc. What I find curious is that these solutions only cause the block to occur earlier or later.

Client code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;


public class TestClientProt {

private static final boolean MAKE_STUB = false;
private static final boolean PING_PONG = false;


private static void send(String value, InputStream rawInput, OutputStream rawOutput) throws IOException {
    PrintWriter output = new PrintWriter(rawOutput);
    output.println(value);
    output.flush();

    if(PING_PONG) {
        BufferedReader input = new BufferedReader(new InputStreamReader(rawInput));
        if(!input.readLine().equals(value))
            System.err.println("Ping pong failed!");
    }
}


private static int receiveInt(InputStream rawInput, OutputStream rawOutput) throws NumberFormatException, IOException {
    return Integer.parseInt(receiveString(rawInput, rawOutput));
}

private static String receiveString(InputStream rawInput, OutputStream rawOutput) throws IOException {
    BufferedReader input = new BufferedReader(new InputStreamReader(rawInput));
    String value = input.readLine();

    if(PING_PONG) {
        PrintWriter output = new PrintWriter(rawOutput);
        output.println(value);
        output.flush();
    }

    return value;
}


public static void main(String[] args) throws IOException {
    Socket socket;

    InputStream rawInput;
    OutputStream rawOutput;

    if(MAKE_STUB) {

        rawInput = new PipedInputStream();
        final OutputStream rawServerOutput = new PipedOutputStream((PipedInputStream) rawInput);

        final PipedInputStream rawServerInput = new PipedInputStream();
        rawOutput = new PipedOutputStream(rawServerInput);

        new Thread() {

            public void run() {
                try {
                    TestServerProt.startSession(rawServerInput, rawServerOutput);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }.start();


    } else {

        InetAddress ip = InetAddress.getByName("127.0.0.1");
        int port = 6789;

        InetSocketAddress serverAddress = new InetSocketAddress(ip, port);      
        socket = new Socket();
        socket.connect(serverAddress);

        rawInput = socket.getInputStream();
        rawOutput = socket.getOutputStream();

    }


    System.out.println("start checkout");

    send("CHKT repname", rawInput, rawOutput);
    System.out.println(receiveInt(rawInput, rawOutput));

    send("RREV", rawInput, rawOutput);
    System.out.println(receiveInt(rawInput, rawOutput));
    System.out.println("receiving revision");
    System.out.println(receiveString(rawInput, rawOutput));
    System.out.println("revision received");

    System.out.println(receiveInt(rawInput, rawOutput));
    System.out.println("receiving files");

    send("RFIL", rawInput, rawOutput);
    System.out.println(receiveInt(rawInput, rawOutput));
    System.out.println("receiving file: ");
    System.out.println(receiveString(rawInput, rawOutput));

    System.out.println("success!");


    if(!MAKE_STUB)
        socket.close();
}

}

Server code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;


public class TestServerProt {

private static final boolean PING_PONG = false;


public static void startSession(InputStream rawInput, OutputStream rawOutput) throws IOException {
    System.out.println("starting session");
    System.out.println(receiveString(rawInput, rawOutput));

    send(101, rawInput, rawOutput);

    System.out.println(receiveString(rawInput, rawOutput));
    System.out.println("sending revision");
    send(104, rawInput, rawOutput);
    send(createRepString(), rawInput, rawOutput);

    System.out.println("sending files");
    send(101, rawInput, rawOutput);
    System.out.println("sending files");

    System.out.println(receiveString(rawInput, rawOutput));
    send(103, rawInput, rawOutput);
    send("content file 1", rawInput, rawOutput);

    System.out.println("success!");

    //send(rCode);
}


private static void send(String value, InputStream rawInput, OutputStream rawOutput) throws IOException {
    PrintWriter output = new PrintWriter(rawOutput);
    output.println(value);
    output.flush();

    if(PING_PONG) {
        BufferedReader input = new BufferedReader(new InputStreamReader(rawInput));
        if(!input.readLine().equals(value))
            System.err.println("Ping pong failed!");
    }
}

private static void send(int value, InputStream rawInput, OutputStream rawOutput) throws IOException {
    send(String.valueOf(value), rawInput, rawOutput);
}


private static int receiveInt(InputStream rawInput, OutputStream rawOutput) throws NumberFormatException, IOException {
    return Integer.parseInt(receiveString(rawInput, rawOutput));
}

private static String receiveString(InputStream rawInput, OutputStream rawOutput) throws IOException {
    BufferedReader input = new BufferedReader(new InputStreamReader(rawInput));
    String value = input.readLine();

    if(PING_PONG) {
        PrintWriter output = new PrintWriter(rawOutput);
        output.println(value);
        output.flush();
    }

    return value;
}


public static void main(String[] args) throws IOException {
    int port = 6789;

    InetSocketAddress serverAddress = new InetSocketAddress(port);
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.bind(serverAddress);

    System.out.println("waiting for client");

    Socket clientSocket = serverSocket.accept();

    System.out.println("client accepted");

    startSession(clientSocket.getInputStream(), clientSocket.getOutputStream());

    serverSocket.close();
    clientSocket.close();
}

private static String createRepString() {
    JSONObject json = new JSONObject();

    json.put("revision_nr", new Integer(5));

    JSONArray jsonArrayContent = new JSONArray();
    JSONObject jsonContent = new JSONObject();

    jsonContent.put("filename", "f1.txt");
    jsonContent.put("revision_nr", 3);
    jsonArrayContent.add(jsonContent);

    jsonContent.put("filename", "f2.txt");
    jsonContent.put("revision_nr", 1);
    jsonArrayContent.add(jsonContent);

    jsonContent.put("filename", "f3.txt");
    jsonContent.put("revision_nr", 5);
    jsonArrayContent.add(jsonContent);

    json.put("content", jsonArrayContent);

    JSONArray jsonArrayStatus = new JSONArray();
    JSONObject jsonStatus = new JSONObject();

    json.put("status", jsonArrayStatus);

    return json.toJSONString();
}

}
Was it helpful?

Solution

In your case, the most important problem is that you create a new BufferedReader every time that you read. The problem is that the the BufferedReader will fill up its entire buffer with available data - because it is supposed to do that. So it will read from the underlying stream beyond the end of the line.

The solution is simple: create the BufferedReader only once, and pass it around, rather than the InputStream.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top