【问题标题】:Parse an infinitley long input in Java在 Java 中解析无限长的输入
【发布时间】:2026-02-09 06:45:01
【问题描述】:

我编写了一个“推送服务器”,当客户端打开连接时,它在服务器写入流时保持打开状态。我想知道如何用 Java 编写一个客户端,当它通过这个无限长的响应接收命令时对命令做出反应。

问题是我不知道从哪里开始。如果您能指出我可以阅读 Javadoc 的课程的方向,那就太好了。我希望它能够立即响应,所以线程可能是必须的。

非常感谢您的帮助!

编辑:无限长度是指未知长度。无限这个词被用来强调连接通常永远不会被服务器关闭。传输的数据并不多。

另一个编辑: 指出这是一个运行 PHP 脚本的 HTTP 服务器可能会有所帮助。

【问题讨论】:

  • “无限长”我希望你的意思是“未知长度”。请不要用无限的数据为我们其他人阻塞互联网!
  • 这个流的格式是怎样的?它是基于 XML 的命令还是基于非文本的命令?可能值得研究 RMI 或类似的。
  • @ptay89 - 每个单独的命令由换行符“\n”分隔,每个命令本身的格式为“command arg1#arg2#arg3...
  • 好吧,套接字似乎是一个合理的解决方案。解析每一行并运行命令的相关代码。

标签: java inputstream server-push


【解决方案1】:

如果你打开一个到服务器的连接,那检查服务器并使用while(server.isSending) { respond();}然后关闭连接?

例子:

//Connection to the server
openConnection();

while(server.isSending()) {

    //Here can you respond in any way
    respond();
}

您可能还必须使用InputStream

我对 InternetConnections 的工作不太熟悉,但我希望你走在正确的轨道上

【讨论】:

  • 我看不出这个答案让我们走得很远。您至少需要公开您的神奇 isSending() 方法应该做什么。如果您不熟悉该主题,则很难理解您为什么要真正回答。
  • 在@Mylleranton 的辩护中,他包含的代码比 OP 更多。不过,这个答案并不是很有帮助。
  • @JesseWebb 包含比 OP 更多的代码不会增加任何价值,除非代码这样做,但它没有。这只是一个没有实际内容的通用答案。关于“可能”必须使用 InputStream 的猜测尤其徒劳。
  • @EJP 线程的创建者请求指导,并且以我目前的知识,我给了他我所知道的。 isSending() 方法可以是 InputStream 方法; int bytesRead = 0; while(bytesRead = in.read(buffer)) >= 0)
  • @Mylleranton 我们可以看到你做了什么。仅此一项并不能使它有用。您刚刚发布的代码不正确。情况并没有好转。如果您有一些您知道有效的具体建议,请务必提供。如果您不熟悉该主题,则应该阅读而不是发布。
【解决方案2】:

如果它们是由换行符分隔的简单命令,那么滚动您自己的客户端应该相当简单:

编辑: HTTP 不是保持长期连接打开的最佳协议,您可能会遇到超时问题。下面的代码未经测试,但应该给你一个很好的起点。

编辑 2: 正如 cmets 中所指出的,线程池在这里可能是必要的或有用的,也可能不是。您应该注意,使用线程不会神奇地使事情变得更快或增加您可以处理的请求数量。使用线程池的好处是允许客户端在前一个命令仍在执行时立即接收新命令。如果处理命令需要一些 I/O 任务,这可能很重要,例如呼叫另一台服务器,但在您的情况下,这可能是矫枉过正。

ExecutorService svc = Executors.newCachedThreadPool();

URLConnection c = 
    new URL("http://192.168.1.122/push/out.php?nduid=1").openConnection();
c.setReadTimeout(30 * 60 * 1000); // Set the read timeout to 30 minutes
c.connect();
InputStream is = c.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));

String cmd = null;
while ((cmd = r.readLine()) != null) {
  svc.execute(new Runnable() {
    public void run() {
      processCmd(cmd); // work happens here in a different thread.
    }
  });
}

is.close();

这里的关键是,只要底层 TCP 连接保持打开状态,从套接字返回的输入流就会保持打开状态。客户端可以继续从流中读取,readLine 调用将在必要时阻塞,直到新数据到达。当readLine返回null时,表示底层输入流遇到文件尾,进而表示服务器关闭了socket。

一旦你有了完整的一行数据,你就可以将它提交到线程池中,以便在不同的线程中解析和执行。以这种方式分解工作可以让您专注于从单行文本中解析和执行命令,这比尝试处理任意大的数据流更易于管理。

【讨论】:

  • 那是因为"192.168.1.122/push/out.php?nduid=1" 是 URL,而不是主机名。在这种情况下,主机名是“192.168.1.122”——但如果您使用的是 HTTP,那么原始套接字并不是客户端的最佳方式。
  • 你的解决方案在线程和所有方面似乎真的很好。有没有办法通过套接字连接发送 HTTP 请求?
  • 顺便说一句,这个解决方案不会扩展(提示:为每个请求创建一个线程是个坏主意)
  • 我同意。 OP 中没有任何内容规定每个请求的线程或线程池。事实上,这些请求似乎非常零星。
  • @miniBill 线程应该被池缓存和重用。线程池可能需要也可能不需要,具体取决于请求的处理时间相对于请求之间的时间量。