我不得不修改一些客户端代码,因为没有正确定义通信协议。
我发现来自服务器的tcp消息会在新行中终止,所以我使用reader.readLine()来读取我的数据。
现在我被告知事实并非如此,而且消息的前4个字符是消息长度,然后我必须阅读消息的其余部分。
最有效的合理方法是什么?
我的一般想法如下:
以下是代码示例(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...
}
我知道怎么做,但我是否要担心字符缓冲区没有被填充?如果是这样我该如何应对这种情况呢?
答案 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”,那么其他一些方法可能更好,没有从字节到字符。