Java BufferedReader将哪个字符解释为流的结尾?
当使用从套接字读取时,它声明readLine方法返回 包含行内容的字符串,不包括任何行终止字符,如果已到达流的结尾,则为null 它怎么知道它已经到达了溪流的尽头?它使用什么字符序列来确定这一点 我想模拟发送相同的字符序列来正确关闭另一个使用管道流的连接 编辑: 这是有问题的代码。从响应来看,似乎没有这样的序列,在PipedOutput流上调用close应该解除对输出流上读线的阻塞。它目前似乎没有这样做,这就是为什么我感到困惑,所以我想它可能是其他地方的一个bug 当inputLine=incomingEventIn.readLine被阻塞时,incomingEventIn.close行似乎被阻塞。如果inputLine=incomingEventIn.readLine未在另一个线程上执行,则incomingEventIn.close将正常执行。为什么会这样Java BufferedReader将哪个字符解释为流的结尾?,java,multithreading,sockets,stream,bufferedreader,Java,Multithreading,Sockets,Stream,Bufferedreader,当使用从套接字读取时,它声明readLine方法返回 包含行内容的字符串,不包括任何行终止字符,如果已到达流的结尾,则为null 它怎么知道它已经到达了溪流的尽头?它使用什么字符序列来确定这一点 我想模拟发送相同的字符序列来正确关闭另一个使用管道流的连接 编辑: 这是有问题的代码。从响应来看,似乎没有这样的序列,在PipedOutput流上调用close应该解除对输出流上读线的阻塞。它目前似乎没有这样做,这就是为什么我感到困惑,所以我想它可能是其他地方的一个bug 当inputLine=inco
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方法。检查此问题:
它怎么知道它已经到达了溪流的尽头?它使用什么字符序列来确定这一点
答案取决于操作系统,但我熟悉的操作系统不读取EOF字符。操作系统向底层调用方返回指示流文件描述符已达到EOF的返回值。JVM看到返回值并返回相应的返回null,-1。。。发送到InputStream或读取器调用程序,具体取决于方法
我想模拟发送相同的字符序列来正确关闭另一个使用管道流的连接
如果您正在从PipedWriter读取,则关闭关联的PipedWriter。读取器或InputStream随后将向调用者返回适当的EOF值
编辑:
由于您的IncomingEventThread正在读取incomingEventIn,因此disconnect方法应首先关闭incomingEventOut。螺纹应封闭其内侧。然后,您应该关闭响应
我不会让线程调用断开连接。。。。它应该只关闭它的读写器,而不是所有的流。从您的角度来看,只需在用于连接到测试的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( );
}
没有。操作系统通过文件大小、TCP FIN位或其他带外机制(取决于源)知道流何时到达其末端。我所知道的唯一例外是,终端驱动程序在通过键盘键入时会将Ctrl/d或Ctrl/z识别为EOF,但这同样是操作系统,而不是Java流或读卡器。在我的pipedreader上调用close似乎不会使readLine返回null并触发解除阻塞。应该吗?“现在它似乎什么也不等了。”汤姆。您应该真正提供您已经拥有的代码。关闭读取器将导致IOException。关闭编写器以读取EOF。我现在已经添加了代码。起初我并不认为这是相关的,我仍然不确定,因为我认为这是我在某处犯的一个编码错误。目前我正在调用PipedReader上的close,readLine似乎仍然没有返回null。我想我在什么地方犯了个错误。谢谢。你打电话给我相信的writer@TomJenkinson@OS不会“关闭输入流”。它通过返回值告诉读者EOS已经发生。应用程序必须关闭流。如果操作系统关闭流,您将永远不会看到“太多打开的文件”。但在我的代码中,当我调用incomingEventIn.close时,incomingEventIn.readLine在另一个线程中似乎没有返回null,并且incomingEventIn.close似乎正在阻塞。我不知道为什么。我已经更新了我的答案。主线程应该关闭2个流,线程应该关闭每个流的另一侧@TomJenkinson。永远不要忽略异常,永远不要用自己的消息替换异常中的消息。请修复代码以记录所有异常并重新运行。你可能会得到一个惊喜。注意:为什么每个插座都有两个螺纹和四个管道?这是
伊姆斯的设计出奇地过于复杂。在Java的16年中,我只使用过一次管道,为了排队,我匆忙地将它们删除了。那里只有2个管道,不是吗?这是我唯一能想到的将从套接字读取的输入拆分,并根据json中声明的类型以两种不同的方式发送它的方法。如果有更好的办法,请告诉我?我知道忽略异常是个坏主意,但如果在这些地方有异常,我真的不知道该怎么办,因为目标是无论如何关闭套接字。两个管道,四个流,加上套接字输入和输出流,每个套接字总共六个。你肯定不需要这些吗?我不关心您如何编写代码,但是如果您在这里展示忽略异常的代码,那么很可能您刚刚错过了抛出的异常。消除这种可能性是你们义不容辞的责任。