【问题标题】:new ObjectInputStream causing hang/timeout新的 ObjectInputStream 导致挂起/超时
【发布时间】:2015-10-20 17:38:29
【问题描述】:

我知道这个问题已经被问过好几次了。但是,在遵循所有其他问题的建议后,我仍然不知道问题可能是什么。

我有一个服务器和一个客户端。一个简单的乒乓球程序。在运行服务器和客户端并给它一些时间来运行它的过程之后,超时异常开始时不时地抛出......

超时是为了防止阻塞,但是,如果删除,它会导致程序停止。

有没有办法防止这种情况发生?

Server.java

public static void main(String args[]) {
        try {
            ServerSocket serverSocket = new ServerSocket(3000);
            while (true) {
                Socket socket = serverSocket.accept();
                String message = null;
                try {
                    socket.setSoTimeout(3000);
                    try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
                        message = (String) objectInputStream.readObject();
                        System.out.println("Server received " + message);
                    }
                    socket.close();
                } catch (IOException | ClassNotFoundException ex) {
                    //This exception is thrown because it hangs, but why does it hang?
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
                if ((message != null) && (message.equals("Ping"))) {
                    try {
                        Socket pongSocket = new Socket("localhost", 3000);
                        pongSocket.setSoTimeout(3000);
                        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pongSocket.getOutputStream())) {
                            objectOutputStream.writeObject("Pong");
                            objectOutputStream.flush();
                            System.out.println("Server sent Pong");
                        }
                        pongSocket.close();
                        continue;
                    } catch (IOException ex) {
                        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

Client.java

public static void main(String args[]) {
        while (true) {
            try {
                Socket pingSocket = new Socket("localhost", 3000);
                pingSocket.setSoTimeout(3000);
                try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pingSocket.getOutputStream())) {
                    objectOutputStream.writeObject("Ping");
                    System.out.println("Client sent Ping");
                    objectOutputStream.flush();
                }
                pingSocket.close();
            } catch (IOException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

【问题讨论】:

  • 除了 flush() 和 swift 部分,看看这个问题:stackoverflow.com/questions/33106127/…。一旦 serverSocket.accept() 调用创建一个新的 Socket,我更喜欢创建一个线程来处理 IO。创建一个线程并传递客户端 Socket 并在那里做你的 IO

标签: java


【解决方案1】:

您对服务器和客户端如何使用套接字感到困惑。您可能可以在 stackoverflow 上找到一堆示例,甚至可以通过 Google 找到更多示例,但一般习惯用法是:

server:
    create server socket
    call accept on server socket
    with accepted socket
        read request from socket
        write response to socket
        close accepted socket
        loop back to accept

client:
    create socket
    call connect on socket
    write request to socket
    read response from socket
    close socket

(Java 会自动为您完成其中的一些操作,例如,在创建套接字并指定主机和端口时,Socket 类会为您调用 connect。)

在您的服务器中,您在读取请求后关闭接受的套接字,然后创建并连接到一个新套接字以发送响应,该套接字会将响应发送到 localhost:3000 上正在侦听的任何内容,即 你的服务器。此外,在您的客户端中,您正在编写请求但不读取响应,并且这样做是在一个紧密的循环中,因此您正在创建大量与服务器的连接,这将很快填满接受积压。

真正的生产应用程序会在服务器中使用线程,甚至使用更高级别的库,甚至像 Tomcat 这样的整个服务器,但归根结底,它们基本上都是在做上述事情,所以理解这一点很好。

为了演示,你的代码应该是这样的:

Server.java

public static void main(String args[]) {
    try {
        ServerSocket serverSocket = new ServerSocket(3000);
        while (true) {
            Socket socket = serverSocket.accept();
            socket.setSoTimeout(250);
            String message = null;
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            try {
                message = (String) objectInputStream.readObject();
                System.out.println("server read: " + message);
            } catch (IOException | ClassNotFoundException ex) {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            }
            if ((message != null) && (message.equals("Ping"))) {
                try {
                    objectOutputStream.writeObject("Pong");
                    System.out.println("server sent pong");
                } catch (IOException ex) {
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            objectInputStream.close();
            objectOutputStream.close();
            socket.close();
        }
    } catch (IOException ex) {
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
    }
}

Client.java

public static void main(String args[]) {
    while (true) {
        try {
            Socket socket = new Socket("localhost", 3000);
            String message;
            socket.setSoTimeout(250);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject("Ping");
            System.out.println("client sent ping");
            try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
                message = (String) objectInputStream.readObject();
                System.out.println("client read: " + message);
            }
            objectOutputStream.close();
            socket.close();
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

【讨论】:

  • 那么解决办法是什么?
  • 这不仅仅是一个简单的修复,您需要修改您的代码才能像上面那样工作。例如,在Server.java中,读取请求后,不要立即关闭套接字然后打开一个新的。相反,检查请求然后发送适当的响应,然后关闭套接字并循环回接受。
  • @ravindra 感谢您进行研究,我向您挥手致意“您可能会找到一堆示例”。 :-) 我确实在这里搜索过,但我在搜索中指定了 Java,而您指向的答案是关于 Swift 的问题,尽管答案是一个很好的一般答案,所以值得一看。
【解决方案2】:

您无法限制发件人的速度。乍一看,我会说您发送请求的速度比处理请求的速度更快,从而使服务器不堪重负。您在套接字上侦听的“服务器”无法与未受限制的“客户端”竞争。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-24
    • 1970-01-01
    • 2014-05-11
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多