【问题标题】:Java Socket connection only works in debug modeJava Socket 连接仅在调试模式下工作
【发布时间】:2016-08-24 13:14:15
【问题描述】:

我想实现一个服务器,它在特定端口上无休止地侦听以接收来自许多客户端的数据(从不并行,只有串行)。我尝试的第一件事是运行服务器,然后串行启动几个客户端(一个接一个)。

这听起来很容易实现,但实际上我遇到了问题,代码只有在服务器代码中至少有一个断点的调试模式下运行时才有效(但与正常运行时相同的错误)断点),对我来说很奇怪。

但是这里是服务器代码:

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try {
                    int counter = 0;
                    ServerSocket serverSocket = new ServerSocket(port);

                    while(true) {
                        System.out.println("Waiting for client...");
                        Socket socket = serverSocket.accept();
                        System.out.println("Accepted");
                        InputStream inputStream = socket.getInputStream();
                        ObjectInputStream objectStream = new ObjectInputStream(inputStream);
                        while(inputStream.available() > 0 ) {
                            String to = (String)objectStream.readObject();
                            System.out.println(to);
                            System.out.println(++counter);
                        }
                        objectStream.close();
                        inputStream.close();

                        System.out.println("Closing socket");
                        socket.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

这里是客户端代码:

public class TaskSenderClient {
  public static void main(String args[]){
    try{
        Socket s = new Socket("localhost",2003);
        OutputStream os = s.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);

        oos.writeObject("test");
        oos.close();
        os.close();
        s.close();
    }catch(Exception e){
        System.out.println("Client exception");
        e.printStackTrace();
    }
  }
}

这是在调试模式下运行时的控制台输出,断点位于服务器代码行System.out.println("Accepted");

Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
test
2
Closing socket
Waiting for client...
Accepted
test
3
Closing socket
Waiting for client...

以及在正常模式下运行/在调试模式下没有断点时的输出:

Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...

我没有得到任何例外...有人可以帮忙吗?这是我第一次尝试在 java 中重用套接字连接。

编辑:检查 inputStream.available 会返回不同的值

我刚刚在服务器代码中的while 之前添加了一个System.out.println(inputStream.available());。这打印出来

  • 始终7 处于带断点的调试模式
  • 7(第一次运行)和0(在所有其他尝试中)之后在非调试模式/没有断点

编辑 2:首先等到 inputStream.available != 0 这个解决方案也适用于我。但是,我在这里删除了这段代码 sn-p,因为检查 available() 似乎不是正确的方法! -> 查看解决方案!

编辑 3:新的服务器代码,它使用 NonEmptyInputStream 检查每个 PushbackInputStream 的非空流:

由于这使用了EOFException,因此对我来说似乎不是最佳解决方案,因此我还删除了此代码 sn-p(而不是参见下面的解决方案)。在下面的 cmets 中讨论了“正确”代码中异常的使用...

【问题讨论】:

  • 哦,是的!你完全正确(评论似乎已经被再次删除)......看起来服务器代码“太快”并且过早地检查可用数据!我刚刚在accept 方法之后添加了一个Thread.sleep(100),一切似乎都正常!谢谢,这拯救了我的一天!
  • 不,不要那样做,这是非常糟糕的做法,而且非常不可靠。只需按照我下面描述的正确方法进行操作
  • 一定要使用java.io吗?为什么不使用 java.nio 方法。网上有很多教程。
  • @D.Müller 一个快速的服务器通常是一件好事。正确编码,而不是添加一些任意延迟。
  • 你能在问题中检查我的“编辑 2”吗?这对我来说很好,但我不确定这是否正确。

标签: java sockets debugging


【解决方案1】:

如果还没有数据,InputStream.available() 可以返回0,这意味着客户端还没有发送一些数据,或者至少还没有到达。如果您添加断点,客户端将有更多时间发送数据。

您可以添加逻辑,例如您的客户端首先发送它写入的对象数量,服务器读取数量,然后在停止读取之前读取那么多对象。

另一种可能性是在ObjectInputStreamInputStream 之间插入PushbackInputStream,然后在PushbackInputStream 上执行read(),检查-1 的结果,这意味着流结束如果不是-1,则使用unread() 将读取的字节推回流中,然后再使用ObjectInputStream 方法。

这里有一个使用最后一个模式重写的原始发布课程的示例:

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try {
                    int counter = 0;
                    ServerSocket serverSocket = new ServerSocket(port);

                    while(true) {
                        System.out.println("Waiting for client...");
                        Socket socket = serverSocket.accept();
                        System.out.println("Accepted");
                        InputStream inputStream = socket.getInputStream();
                        PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
                        ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream);
                        for(int i; (i = pushbackInputStream.read()) != -1;) {
                            pushbackInputStream.unread(i);
                            String to = (String) objectStream.readObject();
                            System.out.println(to);
                            System.out.println(++counter);
                        }
                        objectStream.close();
                        pushbackInputStream.close();
                        inputStream.close();

                        System.out.println("Closing socket");
                        socket.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

或者在这里再次尝试使用资源,这比手动关闭AutoClosables 更可取。

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try (ServerSocket serverSocket = new ServerSocket(port)) {
                    int counter = 0;

                    while(true) {
                        System.out.println("Waiting for client...");
                        try (Socket socket = serverSocket.accept();
                             InputStream inputStream = socket.getInputStream();
                             PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
                             ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream)) {
                            System.out.println("Accepted");
                            for(int i; (i = pushbackInputStream.read()) != -1;) {
                                pushbackInputStream.unread(i);
                                String to = (String) objectStream.readObject();
                                System.out.println(to);
                                System.out.println(++counter);
                            }
                            System.out.println("Closing socket");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

【讨论】:

  • 现在对我有用,非常感谢!在我使用 PushbackInputStream 遇到问题之前,它是未读方法(我总是因为错误/无效的标头而遇到异常。
【解决方案2】:

available() 不是流结束的有效测试。请参阅 Javadoc。您应该从对象流中读取直到EOFException 被捕获。

【讨论】:

  • 好吧,我的可用调用甚至不会一直返回 0,只有当我运行进一步的客户端请求时(并且它们不是处于调试模式)...
  • 好的,所以你没有在对象输入流上调用它。这仍然是不正确的技术。它不能满足您的用途,
  • @D.Müller 看看我的回答,它解释了为什么 available() 是错误的使用方法。 JavaDoc 当然也告诉它。
  • @EJP 等待EOFException 也不是最好的技术,因为流控制的异常是不好的做法,而且由于异常机制的启动而慢得要命。测试 EOS 确实是正确的技术,如果做得正确。 ;-)
  • 虽然没有必要对一个完全有效的答案投反对票。事实上,异常机制需要很多性能,并且最好进行预先检查而不是等待异常。这不仅是抛出异常,将代码放在 try-catch 块中还可以防止编译器在其他情况下进行的各种优化。如果你想要更多的推理和数字,你可以 e。 G。正如 Joshua Bloch 所说,阅读“Effective Java”:异常,顾名思义,仅用于异常情况;它们不应该用于普通的控制流。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多