在Java中,为什么readLine()会阻塞封闭的套接字连接?
我有一个简单的客户端/服务器应用程序。服务器设置为,如果N秒内没有数据进入,则会发生超时并关闭套接字连接。我通过Socket.setSoTimeout()完成此操作。如果客户端挂起,所有这些都可以正常工作。但是,如果客户端死了(例如,我用Ctrl-C杀死它),那么readLine()永远不会超时 以下是服务器代码,如果有区别的话:在Java中,为什么readLine()会阻塞封闭的套接字连接?,java,timeout,bufferedreader,Java,Timeout,Bufferedreader,我有一个简单的客户端/服务器应用程序。服务器设置为,如果N秒内没有数据进入,则会发生超时并关闭套接字连接。我通过Socket.setSoTimeout()完成此操作。如果客户端挂起,所有这些都可以正常工作。但是,如果客户端死了(例如,我用Ctrl-C杀死它),那么readLine()永远不会超时 以下是服务器代码,如果有区别的话: public void run() { PrintWriter out = null; BufferedReader in = null;
public void run()
{
PrintWriter out = null;
BufferedReader in = null;
try {
sock.setSoTimeout(10000);
out = new PrintWriter(sock.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String input;
while ((input = in.readLine()) != null) {
我曾尝试将信号处理程序放入客户端,以便将异常终止消息发送到服务器,但这不起作用(我怀疑在发送异常终止之前套接字已关闭,但我还没有真正花时间尝试解决这一问题)
有没有办法定期唤醒并检查套接字状态以查看其是否已关闭?或者(更好的是)如果套接字关闭,就没有readLine()挂起?我应该使用某种无缓冲阅读器吗?是否存在支持readLine类机制的无缓冲读取器
我正在Linux上使用Java6
编辑:我自己在空闲时间杀死客户;此时已发送和接收所有数据。我已经验证(通过ps)客户端程序不再运行;Java进程确实已经被杀死了。使用netstat,我可以看到套接字两端都已关闭,但readLine()仍然挂起。根据,readLine()方法将抛出IOException。根据IOException,异常应该在中断时抛出。如果不编写代码,我会说您可以通过捕获
IOException
来处理流过早关闭的情况,我认为您已经误诊了这个问题
如果套接字的输入端已关闭,则readLine()
调用应看到该关闭。在使用了所有缓冲数据之后,调用应该返回一个null
。(如果套接字正常关闭,则不会出现IOException,但如果连接超时,则可能会出现IOException……例如。)
最明显的解释是插座尚未关闭。看看另一端将关闭套接字的情况,并找出为什么还没有发生这种情况
这些都不是解决方案,但我将回答完整性问题 有没有办法定期唤醒并检查套接字状态以查看其是否已关闭 另一个线程可以做到这一点,但这个线程不行 或者(更好的是)如果套接字关闭,就没有readLine()挂起 这就是应该发生的事情 我应该使用某种无缓冲阅读器吗 这没用。您将不得不自己实现
readLine()
,代码将在同一点阻塞。阻塞行为发生在读卡器链的缓冲层和字符解码层下面
是否存在支持readLine类机制的无缓冲读取器
不,没有。参见上文。当你花费数小时去寻找一个不存在的问题时,你难道不讨厌吗?在我尝试为Stephen C提供SSCCE时,我发现我无法提供这样的示例,因为它正在工作。我的调试代码正在创建问题 作为记录,这里是工作代码(以防其他人有类似问题): sscceServer.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class sscceServer
{
public static class EchoHandlerThread implements Runnable
{
private final Socket sock;
private final int id;
public EchoHandlerThread(Socket s, int i)
{
sock = s;
id = i;
}
public void run()
{
try {
//----- set socket read timeout to 10 seconds -----
sock.setSoTimeout(10000);
PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String input;
while ((input = in.readLine()) != null) {
System.out.println("[" + id + "] <" + input + ">");
}
out.close();
in.close();
sock.close();
System.out.println("\tConnection #" + id + " closed");
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
public static class Listener
{
private final InetAddress bindip;
private final int portnum;
public Listener(String ipstr, int pn)
{
try {
bindip = InetAddress.getByName(ipstr);
portnum = pn;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void start()
{
try {
ServerSocket srvsock = new ServerSocket(portnum, 0, bindip);
System.out.println("Listening on " + bindip.getHostAddress() + ":" + portnum);
int connum = 0;
while (true) {
Socket sock = srvsock.accept();
connum++;
InetAddress ipaddr = sock.getInetAddress();
System.out.println("Connection #" + connum + " from: " + ipaddr.getHostAddress());
new Thread(new EchoHandlerThread(sock, connum)).start();
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
public static void main(String[] args)
{
Listener lsnr = new Listener("localhost", 8988);
lsnr.start();
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public final class sscceClient
{
public static void main(String[] args)
{
try {
Socket sock = new Socket("localhost", 8909);
PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
out.println("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
out.println("Ut sem ante, mollis ut porttitor at, aliquet vel nulla.");
out.println("Suspendisse quis sapien ante, a cursus nunc.");
//---- sleep for 20 seconds -----
Thread.sleep(20000);
out.close();
in.close();
sock.close();
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
谢谢你的帮助 请看-也许客户机在CR发送后被杀死,而readLine()从未消耗LF,因此它永远在为它哭泣。我自己在空闲期间杀死了客户机;此时已发送和接收所有数据。什么样的套接字?TCP或UDP?“最明显的解释是套接字尚未关闭。”——客户端程序不再运行;Java进程已被终止。使用netstat,我可以看到套接字两端确实是关闭的,但readLine()仍然挂起。我将在上面添加此信息。您可以为此提供SSCCE吗?您报告的内容与我的经验相反…请参阅下面的工作代码。谢谢你的帮助,很抱歉浪费了你的时间。问题是,中断永远不会被抛出。