[英]readline() blocks when using BufferedReader over Java input/output streams
我正在制作一個Java(版本控制)應用程序,其中客戶端和服務器通過套接字流進行通信。 我遇到的問題是,客戶端或服務器在執行readline()時經常傾向於阻塞,最終也會導致另一端也阻塞。 奇怪的是,第一次運行似乎沒有發生該錯誤。
我花了很多時間來找出導致問題的原因以及如何解決問題,但是我只能將問題簡化為下面提供的代碼。 MAKE_STUB參數允許使用管道流而不是套接字流。 它通常會導致在客戶端完成讀取之前關閉線程時導致的錯誤,但是有時也會發生與使用套接字時相同的錯誤,這大概表明問題不是由套接字的濫用引起的,而是流的濫用引起的。 ?
我還嘗試了以下操作:更改刷新輸出的時間,在println()中添加+“ \\ n”,依次發送/接收(PING_PONG),防止流被垃圾收集(使用靜態列表),發送一個字符串而不是JSON等。我發現奇怪的是,這些解決方案只會導致該塊發生在更早或更晚的時間。
客戶代碼:
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();
}
}
服務器代碼:
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();
}
}
對於您而言,最重要的問題是每次閱讀時都創建一個新的BufferedReader
。 問題在於BufferedReader將用可用數據填充其整個緩沖區-因為它應該這樣做。 因此,它將從該行末尾的基礎流中讀取。
解決方案很簡單:只創建一次BufferedReader
,然后傳遞它,而不是InputStream
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.