【问题标题】:Java message is sent to a closed socketJava 消息被发送到关闭的套接字
【发布时间】:2016-01-06 09:50:39
【问题描述】:

我有一个客户端程序,它将在控制台中键入的消息发送到服务器。根据一些建议,我使用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

谁能解释这种行为并告诉我一种在第一次尝试发送无效消息时立即获得通知的方法?

【问题讨论】:

    标签: java sockets


    【解决方案1】:

    根据一些建议,我使用Socket.checkError(). 引入了对关闭套接字的检查

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

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

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

    谁能解释这种行为并告诉我一种在第一次尝试发送无效消息时立即获得通知的方法?

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

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

    【讨论】:

      【解决方案2】:

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

      来自javadoc:

      注意:关闭套接字并不会清除其连接状态,这意味着 此方法将为关闭的套接字返回 true(请参阅 isClosed()),如果 关闭前已成功连接。

      要检查连接是否真的关闭,只需调用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();
      

      }

      【讨论】:

      • 完全混乱,至少部分不正确。 Socket.isClosed() 和 Socket.isConnected() 将永远根据对等方对连接所做的操作或网络错误返回 false。他们准确地告诉你并且只告诉你你对这个套接字做了什么:连接它,关闭它。
      • 是的,不要告诉同行做了什么。如果对方关闭了套接字,则无法使用 isClosed() 进行检查
      • 这就是我所说的,这就是在“注意:如果 isConnected 将按照你的意愿工作”之后的答案中的所有内容不正确的原因。你应该删除它。
      • 您向我们展示了在不可能出现的情况下结果如何为 -1,其中 'isConnected() 将按您的意愿工作(如果套接字的另一端关闭连接或如果有网络问题或类似问题)'。事实上,这一段并没有开始有意义。如果它返回false,则不会有任何类型的返回值,更不用说-1了,因为read()不会被执行。
      • 我只是表明,如果用户创建一个函数来在读取或写入之前检查套接字的真实状态,并不能保证后续的读取或写入将起作用
      【解决方案3】:

      从@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) {}
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-13
        • 2015-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-15
        • 1970-01-01
        相关资源
        最近更新 更多