使用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 :感谢Gray's answer。输入流在导致锁定的输出之前被关闭。它需要反过来。首先关闭输出流,然后通知输入流该流已关闭并取消阻止readLine()
方法。
答案 0 :(得分:3)
它是如何知道它已到达流的末尾?它用于确定这一点的字符序列。
这个问题的答案取决于操作系统,但操作系统我很熟悉,没有读取EOF字符。 OS返回底层调用者返回值,指示流(文件描述符)已达到EOF。 JVM查看返回值并根据方法将适当的返回值null
,-1
,...)返回给InputStream
或Reader
调用方。
我想模拟发送相同的字符序列以正确关闭使用PipedStreams的另一个连接。
如果您正在阅读PipedReader
,则会关闭相关的PipedWriter
。然后,Reader
或InputStream
会将相应的EOF值返回给调用者。
修改强>
由于您IncomingEventThread
正在阅读incomingEventIn
,disconnect()
方法应先关闭incomingEventOut
。线程应该关闭内侧。然后你应该关闭响应。
我不有线程调用disconnect(...)
。它应该只关闭它的读者和作者,而不是所有的流。
答案 1 :(得分:2)
答案 2 :(得分:2)
从您的角度来看,只需拨打用于连接测试的close
上的PipedOutputStream
。
套接字的实际close
由客户端和服务器上的TCP堆栈执行。
这应该这样做(注意你不能在同一个线程上读/写管道流,因此有2个方法和一个线程创建):
void runTest ( final PipedInputStream sink ) throws Exception
{
try( final PipedOutputStream stream = new PipedOutputStream( sink ) )
{
try ( final OutputStreamWriter swriter =
new OutputStreamWriter( stream, "UTF-8" )
)
{
try ( final PrintWriter writer = new PrintWriter( swriter ) )
{
writer.println( "Hello" );
writer.println( "World!" );
}
}
}
}
void test ( final PipedInputStream sink ) throws InterruptedException
{
final Thread outputThread =
new Thread(
new Runnable ( )
{
@Override
public void run ( )
{
try
{
runTest( sink );
}
catch ( final Exception ex )
{
throw new RuntimeException( ex );
}
}
}
);
outputThread.start( );
outputThread.join( );
}
答案 3 :(得分:2)
没有一个。操作系统根据文件大小,TCP FIN位或其他带外机制知道流何时到达其末尾,具体取决于源。我所知道的唯一例外是终端驱动程序在键盘类型时将Ctrl / d或Ctrl / z识别为EOF,但同样是操作系统,而不是Java流或读取器。