在Java中读取tcp流的最有效方法

时间:2009-05-07 09:48:15

标签: java tcp

我不得不修改一些客户端代码,因为没有正确定义通信协议。

我发现来自服务器的tcp消息会在新行中终止,所以我使用reader.readLine()来读取我的数据。

现在我被告知事实并非如此,而且消息的前4个字符是消息长度,然后我必须阅读消息的其余部分。

最有效的合理方法是什么?

我的一般想法如下:

  1. 创建一个4字符数组
  2. 读入前4个字符
  3. 确定消息长度
  4. 创建一个新的消息长度数组
  5. 读入新数组。
  6. 以下是代码示例(reader是在其他地方创建的BufferedReader):

    char[] chars = new char[4];
    int charCount = reader.read(chars);
    String messageLengthString = new String(chars);
    int messageLength = Integer.parseInt(messageLengthString);
    chars = new char[messageLength];
    charCount = reader.read(chars);
    if (charCount != messageLength)
    {
        // Something went wrong...
    }
    

    我知道怎么做,但我是否要担心字符缓冲区没有被填充?如果是这样我该如何应对这种情况呢?

3 个答案:

答案 0 :(得分:3)

Java中的Chars用于 text 数据。您确定协议真的以这种方式定义消息的长度吗?更有可能的是,前四个字节表示32位长度。

如果您正在与C或C ++开发人员交谈,他们可能会使用“char”作为“byte”的同义词。

编辑:好的,基于评论:

我会创建一个方法,它接受Reader和一个计数并重复调用read(),直到它读取了正确的数据量或引发异常。像这样:

public static String readFully(Reader reader, int length) throws IOException
{
    char[] buffer = new char[length];
    int totalRead = 0;
    while (totalRead < length)
    {
        int read = reader.read(buffer, totalRead, length-totalRead);
        if (read == -1)
        {
            throw new IOException("Insufficient data");
        }
        totalRead += read;
    }
    return new String(buffer);
}

然后您的代码可以是:

String lengthText = readFully(reader, 4);
int length = Integer.parseInt(lengthText);
String data = readFully(reader, length);
// Use data now

你应该检查当他们想要发送少于1000个(或超过9999个)字符时会发生什么......

答案 1 :(得分:1)

关于问题的一部分,一旦你确定了这个字符,你需要读取一定数量的字符,以下习语在java.io.Readers中是常见的:

int lengthToRead = getRequiredReadLength(); // Left as exercise to reader :-)
char[] content = new char[lengthToRead]
int from = 0;
while (lengthToRead > 0)
{
   try
   {
      int nRead = reader.read(context, from, lengthToRead);
      if (nRead == -1)
      {
         // End of stream reached before expected number of characters
         // read so handle this appropriately - probably throw an exception
      }
      lengthToRead -= nRead;
      from += nRead;
   }
   catch (IOException e)
   {
      // Handle exception
   }
}

由于read调用保证返回非零结果(调用阻塞直到某些数据可用,所以到达流的末尾(返回-1)或抛出异常)这个while循环确保只要流可以提供它们就可以读取所需数量的字符。

一般情况下,只要一次从Reader中要求一个以上的字符,就应该知道不能保证实际提供了那么多字符,并且应该始终检查返回值以查看发生了什么。否则你将不可避免地在某些时候出现bug,你的部分流会“消失”。

答案 2 :(得分:0)

呃......对于Unicode,Java 16位不是char吗?我不认为你做的是正确的事情,使用字符来表示网络中的字节。您应该考虑使用ByteBuffer包中java.nio之类的内容。

如果您知道单个消息的最大大小,那么就不会阻止您创建单个缓冲区,将四个字节读入缓冲区,将它们解析为int左右,然后执行新的读取使用该大小,覆盖缓冲区的内容。

更新:以上假设协议是二进制的,并且使用char是“C-ism”。如果协议实际上是文本,并且最初的4-char长度是一个填充整数(在某个基数中,我猜10?),如“0047”或“6212”,那么其他一些方法可能更好,没有从字节到字符。