6

我有一个简单的客户端/服务器应用程序。服务器设置为如果在 N 秒内没有数据进入,则会发生超时并关闭套接字连接。我通过 Socket.setSoTimeout() 做到这一点。如果客户端挂起,一切正常。但是,如果客户端死了(例如,我用 Ctrl-C 杀死它),那么 readLine() 永远不会超时。

这是服务器代码,如果有区别的话:

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) {

我尝试将信号处理程序放入客户端以将 ABEND 消息发送到服务器,但这不起作用(我怀疑套接字在发送 ABEND 之前已关闭,但我并没有真正花时间试图弄清楚那出来)。

有没有办法定期唤醒并检查套接字状态以查看它是否已关闭?或者(更好)如果套接字关闭,没有 readLine() 挂起?我应该使用某种无缓冲的阅读器吗?是否存在支持类似 readLine 机制的无缓冲读取器?

我在 Linux 上使用 Java 6。

编辑:我在空闲期间自己杀死了客户;此时所有数据都已发送和接收。我已经验证(通过 ps)客户端程序不再运行;Java进程确实被杀死了。使用 netstat 我可以看到套接字在两端都关闭了,但 readLine() 仍然挂起。

4

3 回答 3

1

我认为你误诊了这个问题。

如果套接字的输入端已经关闭,那么readLine()调用应该会看到关闭。在消耗完所有缓冲数据后,调用应该返回一个null. (如果套接字正常关闭,您将不会收到 IOException,但如果连接超时,您可能会收到一个……例如。)

最明显的解释是套接字还没有关闭。看看另一端将关闭套接字的情况,并找出为什么还没有发生这种情况。


这些都不是解决方案,但我会回答完整性。

有没有办法定期唤醒并检查套接字状态以查看它是否已关闭?

另一个线程可以做到这一点,但不是这个。

或者(更好)如果套接字关闭,没有 readLine() 挂起?

这就是应该发生的事情。

我应该使用某种无缓冲的阅读器吗?

它没有帮助。你最终不得不readLine()自己实现,你的代码会在同一点阻塞。阻塞行为发生阅读器链的缓冲和字符解码层之下。

是否存在支持类似 readLine 机制的无缓冲读取器?

不,它没有。看上面。

于 2013-02-07T14:23:09.813 回答
1

当您花费数小时追查不存在的问题时,您不讨厌它吗?在我尝试为 Stephen CI 提供 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();
    }
}

sscceClient.java

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);
        }
    }
}

谢谢您的帮助!

于 2013-02-07T18:33:22.187 回答
0

根据Javadoc,该readLine()方法将抛出 IOException。并且根据 IOException 的Javadoc,异常应该在中断时抛出。在不编写代码的情况下,我会说您可以通过 catch 来处理流过早关闭的情况IOException

于 2013-02-07T14:21:45.630 回答