Java消息被发送到已关闭的套接字

时间:2016-01-06 09:50:39

标签: java sockets

我有一个客户端程序,它将在控制台中输入的消息发送到服务器。根据一些建议,我介绍了对Socket.checkError()的已关闭套接字的检查。然而,由于某种原因,它仅在第二次尝试发送消息失败后才显示错误。

我的代码:

    BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
    while (true)
        try (
            Socket clientSocket = new Socket(hostname, port);
            PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
        ) {
            String input;
                while ((input=stdIn.readLine())!=null) {
                    socketOut.println(input);
                    if (socketOut.checkError()) {
                        System.out.println("Got socket error");
                        break;
                    }
                }
        } catch (IOException e) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e1) {}
        }

收到'message1'后我关闭(手动)我的服务器端。因此,我希望在尝试发送下一条消息时收到错误。然而,它只发生一条消息:

message1
message2
message3
Got socket error

任何人都可以解释这种行为,并建议我在首次尝试无效发送消息时获得通知吗?

3 个答案:

答案 0 :(得分:1)

在java库中,没有方法可以检查是否打开了连接。像isConnected()isClosed()这样的方法只检查连接的一侧(您调用方法的位置)。

来自javadoc

  

注意:关闭套接字并不清除其连接状态,这意味着   对于已关闭的套接字,此方法将返回true(请参阅isClosed())if   它在关闭之前已成功连接。

要检查连接是否已真正关闭,只需调用read()方法(或等效方法)并检查它是否返回-1。

注意:如果isConnected可以正常工作(如果套接字的另一端关闭了连接,或者如果存在网络问题或类似情况,则返回false)序列:

if (socket.isConnected()) {
    int x = socked.read();
}

不会授予x具有不同于-1的值或抛出IOException,因为在isConnected测试之后和读取操作之前可以关闭连接。

以下代码显示如何对套接字进行任何类型的检查都无法保证后续读取将提供有效结果。

// Return true because socket communication is enabled
if (myFunctionToCheckIfSocketIsOpen(socket)) {  

   // Here the peer closed the socket or the network shutdown

   // This read will give -1 or throws IOException also if the previous check returned true
   int x = socket.read();

}

答案 1 :(得分:1)

  

根据一些建议,我介绍了对Socket.checkError().

的已关闭套接字的检查

没有这样的方法。显然,您指的是PrintWriter.checkError().

  

尽管如此,由于某种原因,它仅在第二次尝试发送消息失败后才显示错误。

原因是发送方有一个套接字发送缓冲区,接收方有一个套接字接收缓冲区,并且发送是异步的:因此第一个发送不可能检测到错误。 / p>

  

任何人都可以解释这种行为,并建议我在首次尝试无效发送消息时获得通知吗?

没有。这就是TCP的本质。您正在尝试的是指示应用程序协议错误,答案也在于应用程序协议的范围:不要让对等方关闭套接字,而此端仍然可以发送数据, OR 不允许此端在对等方指示后发送数据,通过应用程序协议,它不会再读取任何数据。

不要在网络上使用PrintWriter。它抑制了实际的异常。使用BufferedWriter以及write()newLine()方法。

答案 2 :(得分:-1)

从@Davide Lorenzo MARINO的回答中我得到了使用read()的想法。它阻止的唯一问题。但是,当read()最终返回-1时,可以始终在另一个线程中运行它,这将修改类全局变量:

static boolean socketIsAlive;

...
    BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
    while (true)
        try (
            Socket clientSocket = new Socket(hostname, port);
            PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
        ) {
            socketIsAlive=true;
            new ConnectionChecker(clientSocket).start();
            String input;
            while (true) {
                if ((input=stdIn.readLine())!=null)
                    socketOut.println(input);
                if (!socketIsAlive) {
                    System.out.println("Got socket error");
                    break;
                }
            }
        } catch (IOException e) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e1) {}
        }
    }
...

static public class ConnectionChecker extends Thread{
    Socket socket;

    public ConnectionChecker(Socket socket) {
        this.socket=socket;
    }

    @Override
    public void run() {
        try {
            if (socket.getInputStream().read()==-1)
                socketIsAlive=false;
        } catch (IOException e) {}
    }
}