TCP Socket接收和处理多条消息

时间:2013-01-11 09:36:10

标签: java database sockets tcp

我在从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);
            }
        }

数据包格式如下:

[HEADER] [DATA] [TERMINATOR]

  • 标题 - &gt;标识消息开头的字符序列(没有关于数据包长度的信息);
  • 数据 - &gt;分为几个部分,如: [Size Seg。 1] [数据段。 1] [尺寸段。 2] [数据段。 2] [尺寸段。 3] [数据段。 3] .... [尺寸段。 N] [数据段。 N]
  • 终结者 - &gt; [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驱动程序)。消息的每个解析都可能涉及对数据库的多个查询。由于我只使用一个线程来接收,解析和存储数据,因此代码的执行速度并不快......应该尽可能快地接收和存储数据。

我想讨论一些问题:

  • 什么是获取所有邮件而不丢失其中一些邮件的最佳方法?
  • 我如何改进数据的接收和存储方式? (数据应尽快存储!)

5 个答案:

答案 0 :(得分: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 ms约为600,000,000个时钟周期。这对计算机来说是永恒的。 :)

上面的代码应该在200毫秒内处理20,000条消息。如果你需要更多,你可以使用NIO,但我不认为你需要。

  

应尽快存储数据!

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

答案 1 :(得分:1)

您正在String制作buffer,对吗?在这种情况下,我建议您修改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中你必须切割未终止的行尾并从方法中返回。

答案 2 :(得分:0)

TCP提供传输服务,而不是数据包服务。为了实现“分组化”,协议必须对分组本身进行帧化。在您的情况下,使用[TERMINTAOR]标记实现框架。在客户端,你应该做的是:

  1. 检查buffer是否包含标记。如果没有,则发出read将数据添加到buffer并返回步骤1.
  2. 解析并使用缓冲区中的数据包
  3. 回到第1步。

答案 3 :(得分:-1)

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

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

答案 4 :(得分:-1)

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