BufferedReaderを使用してソケットから読み取る場合 、readLine()
メソッドが返すことを示します
行終了文字を含まない、行の内容を含む文字列、またはストリームの末尾に到達した場合は null
ストリームの終わりに達したことをどのように知るのでしょうか? これを決定するために使用する文字のシーケンス。
PipedStreams を使用する別の接続を適切に閉じるために、同じ文字シーケンスの送信をシミュレートしたいと考えています。
編集: これが問題のコードです。応答から、そのようなシーケンスはないように見え、PipedOutput ストリームで close() を呼び出すと、出力ストリームで readLine() のブロックが解除されるはずです。現時点ではこれを行っているようには見えないため、混乱したので、どこか別のバグである可能性があると考えています。
何が起こっているかというと、incomingEventIn.close()
がブロックしているときに回線がブロックされているように見えるということinputLine = incomingEventIn.readLine()
です。inputLine = incomingEventIn.readLine()
他のスレッドで実行されていない場合は、正常に実行されincomingEventIn.close()
ます。なぜこうなった?
public class SocketManager {
private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
private PipedOutputStream incomingEventOutStream = null;
private PrintWriter incomingEventOut = null;
private BufferedReader incomingEventIn = null;
private PipedOutputStream incomingResponsOutStream = null;
private PrintWriter incomingResponseOut = null;
private BufferedReader incomingResponseIn = null;
private ArrayList<AsteriskLiveComsEventListener> listeners = new ArrayList<AsteriskLiveComsEventListener>();
private final ExecutorService eventsDispatcherExecutor;
private String ip;
private int port;
private Object socketLock = new Object();
public SocketManager(String ip, int port) {
this.ip = ip;
this.port = port;
eventsDispatcherExecutor = Executors.newSingleThreadExecutor();
}
public void connect() throws UnableToConnectException, AlreadyConnectedException {
synchronized(socketLock) {
if (socket != null && !socket.isClosed()) {
throw (new AlreadyConnectedException());
}
try {
socket = new Socket(ip, port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
incomingEventOutStream = new PipedOutputStream();
incomingEventIn = new BufferedReader(new InputStreamReader(new PipedInputStream(incomingEventOutStream)));
incomingEventOut = new PrintWriter(incomingEventOutStream);
incomingResponsOutStream = new PipedOutputStream();
incomingResponseIn = new BufferedReader(new InputStreamReader(new PipedInputStream(incomingResponsOutStream)));
incomingResponseOut = new PrintWriter(incomingResponsOutStream);
} catch (IOException e) {
throw (new UnableToConnectException());
}
new Thread(new IncomingEventThread()).start();
new Thread(new SocketThread()).start();
}
}
public void disconnect() throws NotConnectedException {
disconnect(false);
}
private void disconnect(boolean notRequested) throws NotConnectedException {
synchronized(socketLock) {
if (!isConnected()) {
throw (new NotConnectedException());
}
try {
incomingEventIn.close();
} catch (IOException e2) {}
// IT NEVER GETS TO HERE!
incomingEventOut.close();
try {
incomingResponseIn.close();
} catch (IOException e1) {}
System.out.println("disconnecting");
incomingResponseOut.close();
try {
socket.shutdownInput();
} catch (IOException e) {}
try {
socket.shutdownOutput();
} catch (IOException e) {}
try {
socket.close();
} catch (IOException e) {}
if (notRequested) {
System.out.println("disconnecting event");
dispatchEvent(new ConnectionLostEvent());
}
}
}
public boolean isConnected() {
synchronized(socketLock) {
return (socket != null && !socket.isClosed());
}
}
public void addEventListener(AsteriskLiveComsEventListener a) {
synchronized(listeners) {
listeners.add(a);
}
}
public void removeEventListener(AsteriskLiveComsEventListener a) {
synchronized(listeners) {
listeners.remove(a);
}
}
private void dispatchEvent(final AsteriskLiveComsEvent e) {
synchronized (listeners) {
synchronized (eventsDispatcherExecutor) {
eventsDispatcherExecutor.execute(new Runnable()
{
public void run()
{
for(int i=0; i<listeners.size(); i++) {
listeners.get(i).onAsteriskLiveComsEvent(e);
}
}
});
}
}
}
public JSONObject sendRequest(JSONObject request) throws JSONException, NotConnectedException {
synchronized(socketLock) {
System.out.println("sending request "+request.toString());
out.println(request.toString());
try {
return new JSONObject(incomingResponseIn.readLine());
} catch (IOException e) {
// lets close the connection
try {
disconnect(true);
} catch (NotConnectedException e1) {}
throw(new NotConnectedException());
}
}
}
private class SocketThread implements Runnable {
@Override
public void run() {
String inputLine = null;
try {
while((inputLine = in.readLine()) != null) {
// determine if this is a response or event and send to necessary location
JSONObject lineJSON = new JSONObject(inputLine);
if (lineJSON.getString("type").equals("response")) {
incomingResponseOut.println(inputLine);
incomingResponseOut.flush();
}
else if (lineJSON.getString("type").equals("event")) {
incomingEventOut.println(inputLine);
incomingEventOut.flush();
}
}
if (isConnected()) {
try {
disconnect(true);
} catch (NotConnectedException e) {}
}
} catch (IOException e) {
// try and disconnect (if not already disconnected) and end thread
if (isConnected()) {
try {
disconnect(true);
} catch (NotConnectedException e1) {}
}
}
}
}
private class IncomingEventThread implements Runnable {
@Override
public void run() {
String inputLine = null;
try {
while((inputLine = incomingEventIn.readLine()) != null) {
JSONObject lineJSON = new JSONObject(inputLine);
String eventType = lineJSON.getString("eventType");
// determine what type of event it is and then fire one that represents it
if (eventType.equals("channelAdded")) {
JSONObject a = lineJSON.getJSONObject("payload");
Hashtable<String,Object> data = new Hashtable<String,Object>();
Object[] keys = a.keySet().toArray();
for(int i=0; i<keys.length; i++) {
data.put((String) keys[i], a.get((String) keys[i]));
}
dispatchEvent(new ChannelAddedEvent(data));
}
else if (eventType.equals("channelRemoved")) {
dispatchEvent(new ChannelRemovedEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("channelsToRoom")) {
ArrayList<Integer> data = new ArrayList<Integer>();
JSONObject a = lineJSON.getJSONObject("payload");
JSONArray ids = a.getJSONArray("channelIds");
for(int i=0; i<ids.length(); i++) {
data.add(ids.getInt(i));
}
dispatchEvent(new ChannelsToRoomEvent(data));
}
else if (eventType.equals("channelToHolding")) {
dispatchEvent(new ChannelToHoldingEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("channelVerified")) {
dispatchEvent(new ChannelVerifiedEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("serverResetting")) {
dispatchEvent(new ServerResettingEvent());
}
}
} catch (IOException e) {}
System.out.println("here");
}
}
編集 2:inputLine = incomingEventIn.readLine()
デバッガーの前にいくつかのブレークポイントを配置すると、正常に実行されてnull が返さ
れるため、どこかでデッドロックの問題だと思います。普通に動かそうとすると固まる。
編集 3:グレイの回答のおかげで解決しました。ロックアップの原因となった出力の前に、入力ストリームが閉じられています。それは逆である必要があります。最初に出力ストリームを閉じると、ストリームが閉じられ、readLine()
メソッドのブロックが解除されたことが入力ストリームに通知されます。