我正在编写一个带有会话线程的服务器,用于与客户端建立开放连接。这相当简单,它只是使用缓冲读取器从输入中读取行。如果该行等于一个表示用户想要退出的特殊值,或者它为空,则循环终止。否则,消息将通过链向上传递到要处理它的模块。出于测试的目的,我只是远程登录到我的服务器并手动输入命令。
如果没问题,除非我通过键入 QUIT 以外的方式终止连接,例如通过关闭终端窗口。然后生成一条具有未知字符序列的消息,并且一条格式错误的消息沿链向上传递。在这个简单的测试用例中,它并不重要,但它确实表明了一个需要解决的问题。
我的代码如下。
public void run () {
BufferedReader inReader;
UpstreamMessage message;
String lastLine;
Thread.currentThread ().setName ("UpstreamThread_" + outer.getId ());
try {
inReader = new BufferedReader (new InputStreamReader (outer.clientSocket.getInputStream (), "UTF8"));
while (!this.ending) {
// Read whatever was in the input buffer
lastLine = inReader.readLine ();
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Last data from input stream reader was \"{0}\"", lastLine);
if (null != lastLine && !lastLine.equals ("QUIT")) {
message = new UpstreamMessage (outer.sessionId, lastLine);
outer.server.acceptMessage (message);
} else {
// End the session
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Thread ending");
break;
}
}
} catch (IOException | IllegalArgumentException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
} catch (InterruptedException ex) {
// This is thrown when we're telling the thread to shut down so it's normal
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
} finally {
this.terminate ();
}
}
当我关闭终端窗口时,我的控制台会为日志记录输出以下内容:
2013 年 7 月 27 日 7:37:00 PM bikeshop.server.Session$UpstreamChannel 运行信息:来自输入流阅读器的最后数据是“”
或者有时,记录器会报告以下内容,表明 lastMessage 变量引用了一个空值,但空值检查失败。
2013 年 7 月 27 日 7:37:00 PM bikeshop.server.Session$UpstreamChannel 运行信息:来自输入流阅读器的最后数据为“null”
我显然做错了什么,但我不知道是什么。我的测试,'if (null != lastLine && !lastLine.equals ("QUIT"))' 没有在我退出终端时捕获缓冲区中的任何内容。如何更优雅地处理这种情况?
编辑:对日志和单步调试的更仔细分析表明,当您退出 telnet 客户端时,它会发送一系列控制字符。这从缓冲区中读取并向上传递,在我的程序的其他部分触发警告。然后会话再次通过它的循环并尝试从现在关闭的套接字中读取。这确实返回 null 并且循环终止。这就是从客户端读取的最后一行是否为空的混淆来源。最后读取的内容确实为空,但重要的是倒数第二个读取,触发异常行为的那个。
所以我想问题已经变成了a)当你退出它时,telnet 客户端会发送什么控制序列,或者b)Java 知道如何检查该控制序列?