【发布时间】:2013-06-19 07:33:42
【问题描述】:
我有一个客户端/服务器应用程序,它使用 BufferedOutputStream / BufferedInputStream 发送/接收数据。通信协议如下:
-
发送部分:
- 第一个字节是要执行的动作
- 接下来的4个字节是消息的长度
- 接下来的 x 个字节(x=消息长度)是消息本身
-
接收部分:
- 读取第一个字节以获取操作
- 读取接下来的 4 个字节以获取消息长度
- 读取 x(在上一步获得)字节以获取消息
现在的问题是,有时当我在服务器部分发送消息的长度(例如:23045)时,我收到一个巨大的整数(例如:123106847)。
一个重要的线索是,这种情况仅在消息超过多个字符(在我的情况下 > 10K)时发生,如果我发送了一条较小的消息(例如 4-5k),一切都会按预期工作。
客户端发送部分(outputStream/inputStream均为BufferedXXXStream类型):
private String getResponseFromServer( NormalizerActionEnum action, String message) throws IOException{
writeByte( action.id());
writeString( message);
flush(;
return read();
}
private String read() throws IOException{
byte[] msgLen = new byte[4];
inputStream.read(msgLen);
int len = ByteBuffer.wrap(msgLen).getInt();
byte[] bytes = new byte[len];
inputStream.read(bytes);
return new String(bytes);
}
private void writeByte( byte msg) throws IOException{
outputStream.write(msg);
}
private void writeString( String msg) throws IOException{
byte[] msgLen = ByteBuffer.allocate(4).putInt(msg.length()).array();
outputStream.write(msgLen);
outputStream.write(msg.getBytes());
}
private void flush() throws IOException{
outputStream.flush();
}
服务器部分(_input/_output 为 BufferedXXXStream 类型)
private byte readByte() throws IOException, InterruptedException {
int b = _input.read();
while(b==-1){
Thread.sleep(1);
b = _input.read();
}
return (byte) b;
}
private String readString() throws IOException, InterruptedException {
byte[] msgLen = new byte[4];
int s = _input.read(msgLen);
while(s==-1){
Thread.sleep(1);
s = _input.read(msgLen);
}
int len = ByteBuffer.wrap(msgLen).getInt();
byte[] bytes = new byte[len];
s = _input.read(bytes);
while(s==-1){
Thread.sleep(1);
s = _input.read(bytes);
}
return new String(bytes);
}
private void writeString(String message) throws IOException {
byte[] msgLen = ByteBuffer.allocate(4).putInt(message.length()).array();
_output.write(msgLen);
_output.write(message.getBytes());
_output.flush();
}
....
byte cmd = readByte();
String message = readString();
任何帮助将不胜感激。如果您需要更多详细信息,请告诉我。
更新:由于 Jon Skeet 和 EJP 的 cmets 我意识到服务器上的读取部分进行了一些毫无意义的操作,但抛开这一点,我终于明白了问题所在:关键是我在应用程序的整个长度内保持流打开,并且前几次我发送了消息长度我可以在服务器端读取它,但是 Jon Skeet 指出数据不会一次全部到达,所以当我尝试读取消息长度时再次,我实际上是从消息本身中读取信息,这就是为什么我有虚假的消息长度。
~而不是发送数据长度然后一次读取它,我发送它没有长度,我一次读取一个字节,直到完美工作的字符串结束
private String readString() throws IOException, InterruptedException {
StringBuilder sb = new StringBuilder();
byte[] bytes = new byte[100];
int s = 0;
int index=0;
while(true){
s = _input.read();
if(s == 10){
break;
}
bytes[index++] = (byte) (s);
if(index == bytes.length){
sb.append(new String(bytes));
bytes = new byte[100];
index=0;
}
}
if(index > 0){
sb.append(new String(Arrays.copyOfRange(bytes, 0, index)));
}
return sb.toString();
}
【问题讨论】:
-
sleep()毫无意义。read()已经阻塞,直到输入可用。而在返回值为 -1 的情况下睡觉和重新阅读就更没有意义了,因为肯定不会再有更多的东西要阅读了。你的意思是while (s != -1)? -
@EJP 你说得对,我知道现在这毫无意义,但撇开主要问题不谈,主要问题是我发送的长度而不是我收到的长度是一些随机情况