0

我在从 TCP 套接字获取所有数据时遇到了一些麻烦。

在我的服务器中,我正在从这样的套接字读取数据:

        int len;
        byte[] buffer = new byte[2000];
        try {
            this.in = new DataInputStream(this.socket.getInputStream());
            this.out = new DataOutputStream(this.socket.getOutputStream());
            running = true;

            while (running) {
                len = in.read(buffer);
                if (len < 0) {
                    running = false;
                } else {
                    parsePacket(buffer, len);
                }
            }

        } catch (IOException ex) {
            System.out.println("Catch IOException: " + ex);
            ex.printStackTrace();
        } finally {
            try {
                System.out.println("Closing");
                in.close();
                out.close();
                socket.close();
            } catch (IOException ex) {
                System.out.println("Finally IOException: " + ex);
            }
        }

数据包格式是这样的:

[标题][数据][终结者]

  • 标头 --> 标识消息开头的字符序列(没有关于数据包长度的信息);
  • 数据 --> 被分割成段,如:[Size Seg. 1][数据段。1][尺寸段。2][数据段。2][尺寸段。3][数据段。3]....[尺寸段。N][数据段。N]
  • 终结者 --> [0x00]

数据接收速度非常快(有时 200 毫秒或更短),因此有时会read(buffer)填充以下buffer消息:

  • [HEADER1][DATA1][TERM1] 或,
  • [HEADER1][DATA1][TERM1][HEADER2][DATA2][TERM2].............[HEADER N][DATA N][TERM N] 或者,
  • [HEADER1][DATA1][TERM1][HEADER2][DATA2][TERM2]......[HEADER N][DAT(最后一条消息不完整)

parsePacket()方法能够解析具有上述格式的消息,如果接下来有更多消息,它们也将被解析(递归)。但是如果最后一条消息不完整,它不会解析它(我不希望这样,但直到现在我还没有找到合适的解决方案)。

消息中的数据存储在 MySQL 数据库中(使用 JDBC 驱动程序)。消息的每次解析都可能涉及对数据库的多个查询。由于我只使用一个线程来接收、解析和存储数据,因此代码的执行速度并没有达到应有的速度……应该尽快接收和存储数据。

我想讨论的几点:

  • 在不丢失一些消息的情况下获取所有消息的最佳方法是什么?
  • 如何改进接收和存储数据的方式?(数据应尽快存储!)
4

5 回答 5

2

由于 TCP 已经是流协议,因此读取此数据的最简单方法是作为流。我会添加一个侦听器来处理事件。

DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

try {
   while(true) {
       listener.startOfMessage();
       for(int segSize; (segSize = dis.readInt()) > 0;) {
          byte[] bytes = new byte[segSize];
          dis.readFully(bytes);
          listener.data(bytes);
       }
       int footer = dis.read();
       // check footer ??
       listener.endOfMessage();
   }
} catch(EOFException endOfStream) {
   // handle or ignore
} finally {
   // close everything.
}

当您自己进行缓冲时,您还必须重新组装消息并保留不完整的消息,这在这里很头疼但没有任何好处。

数据接收速度非常快(有时 200 毫秒或更短)

对于您拥有的每个 CPU,200 毫秒大约是 600,000,000 个时钟周期。这对计算机来说是永恒的。:)

上面的代码应该在 200 毫秒内处理大约 20,000 条消息。如果您需要的不止这些,您可以使用 NIO,但我不认为您需要这样做。

数据应尽快存储!

我怀疑 MySQL 很好,它不是“尽可能快”,但我看不出你所说的不使用它的任何理由。

于 2013-01-11T09:51:32.200 回答
1

你是从 生产Stringbuffer,对吧?在这种情况下,我建议您修改parsePacket方法的接口并将循环转换为以下内容:

        String tail = "";
        String line = "";
        while (running) {
            len = in.read(buffer);
            if (len < 0) {
                running = false;
            } else {
                line = tail + new String(buffer);
                tail = parsePacket(line, len);
            }
        }

在你的parsePacket你必须切断未终止的线尾并从方法中返回它。

于 2013-01-11T09:43:28.633 回答
0

TCP 提供传输服务,而不是数据包服务。为了实现“打包”协议必须自己构建数据包。在您的情况下,框架是使用[TERMINTAOR]标记实现的。在客户端,您应该做的是:

  1. 检查您是否buffer包含标记。如果没有,则发出 aread向您添加数据buffer并返回步骤 1。
  2. 解析并使用缓冲区中的数据包
  3. 返回步骤 1。
于 2013-01-11T09:42:35.970 回答
-1

TCP 是一种流协议。它按照写入顺序将写入一端的套接字的所有字节传递到另一端的套接字。它不能保证它们会以与放入它们的大小相同的“块”到达。读取可能会比任何给定写入中写入的字节多或少。但是所有的字节都在那里,而且它们的顺序都是正确的。

对此的解决方案是使用定义消息边界的协议 - 消息终止符、长度标头或 XML 等自描述格式。

于 2013-01-11T09:44:21.073 回答
-1

TCP 是一种流协议,它不保证从一个端口到另一个相同块大小的消息的大小。在阅读时,您可能会在一次写入中获得或多或少的字节数。

于 2013-01-11T09:55:37.457 回答