2008-03-27
Windows与Linux下InputStream读取字节与字符的不同
最近做了一个报文发送接收解析的客户端,发现了这样的问题:
某程序:
这样的程序,在windows下正常工作,再放到linux下,读完报文头开始读取报文体的时候,并没有从报文体的开头开始读取,而是丢失了报文体前面的一些数据,导致读不到足够的数据直到超时。
经过一番调试和请教后发现原因:在linux下一个InputStream不能用字符流和字节流分段读取,因为linux下的字符由于utf8编码会多加几位,用字符读完报文头后其实就已经多读了许多位,再用字节读就丢掉多读的那部分了。
因此,只要将读取报文头的部分也改成字节流读取就可以了
某程序:
con = new Socket(ip, port);//一个socket
InputStream socketIn = con.getInputStream();
InputStreamReader isr=new InputStreamReader(socketIn, "UTF-8");
while ((headchar = isr.read()) != -1) {// 读取报文头 用字符流来读取
headres += (char) headchar;
......//一些逻辑 处理报文头
}
...
//报文头读完了 获取了报文体的大小等内容 开始读报文体
byte tempbuf[] = new byte[buffsize];
int start = 0;
int tem = -1;
//用字节流来读报文体
while ((tem = socketIn.read(tempbuf, start, buffsize)) != -1) {
log.info("从"+start+"开始读"+buffsize);
log.info("实际读取" + tem);
if (tem < outparamsizes[i]) {
start = start + tem;
buffsize = buffsize - tem;
...//一些逻辑 处理报文体 读取完毕
}
这样的程序,在windows下正常工作,再放到linux下,读完报文头开始读取报文体的时候,并没有从报文体的开头开始读取,而是丢失了报文体前面的一些数据,导致读不到足够的数据直到超时。
经过一番调试和请教后发现原因:在linux下一个InputStream不能用字符流和字节流分段读取,因为linux下的字符由于utf8编码会多加几位,用字符读完报文头后其实就已经多读了许多位,再用字节读就丢掉多读的那部分了。
因此,只要将读取报文头的部分也改成字节流读取就可以了
while ((headchar = socketIn.read()) != -1) {
...}
评论
beckrabbit
2008-03-27
fool_leave 写道
你的结束标示符是什么?
read动作如果返回的是-1,只能表明这次流数据结束,并不是标示你的数据已经到了结尾。换句话说你如果发送出1024个byte,但接收的地方很可能会接收到512个byte之后就读到-1.
这个和操作系统对io缓存的处理也有关系。具体的可能要看IO底层处理了。
即便 while ((tem = socketIn.read(tempbuf, start, buffsize)) != -1) 这句也可能不能正常执行,因为我们并不可以肯定再一次IO流的传输中就可以把全部的数据收到,也可能在还没有读到buffsize大小的数据前就收到了-1。所以应该明确知道自己有多少数据要接收,然后while循环里判断是不是全部收到了,而不是流数据是不是结尾了。
read动作如果返回的是-1,只能表明这次流数据结束,并不是标示你的数据已经到了结尾。换句话说你如果发送出1024个byte,但接收的地方很可能会接收到512个byte之后就读到-1.
这个和操作系统对io缓存的处理也有关系。具体的可能要看IO底层处理了。
即便 while ((tem = socketIn.read(tempbuf, start, buffsize)) != -1) 这句也可能不能正常执行,因为我们并不可以肯定再一次IO流的传输中就可以把全部的数据收到,也可能在还没有读到buffsize大小的数据前就收到了-1。所以应该明确知道自己有多少数据要接收,然后while循环里判断是不是全部收到了,而不是流数据是不是结尾了。
read() Returns:
the total number of bytes read into the buffer, or -1 is there is no more data because the end of the stream has been reached.
数据量大或者网络状况不佳的时候没有办法一次把buffsize(报文体大小)读回来,所以才有了下面的代码
log.info("从"+start+"开始读"+buffsize);
log.info("实际读取" + tem);
if (tem < outparamsizes[i]) {
start = start + tem;
buffsize = buffsize - tem;
根据每次读到的数据位移读取位置和读取量,直到读到的大小=buffsize 结束,while里的-1在正常情况下是不会遇到的。
这里的日志例如:从0开始读10000
实际读取3000
从3000开始读7000
实际读6000
从9000开始读1000
实际读1000
fool_leave 写道
“在linux下一个InputStream不能用字符流和字节流分段读取,因为linux下的字符由于utf8编码会多加几位”
问题不是这个。不论哪个操作系统,都不会在流的数据上加减数据的。用UTF-8编码,也是在收到数据后encode而已。如果读取时设置了reader的编码格式,它会按照这个编码格式来读取word而不是读取byte,不同的编码格式,每个word的长度肯定不一样,这样就会出现将流错误截断的问题了。所以建议你不要将Reader/Writer和IOStream混用。甚至DataInputStream和InputStream在配合使用的时候也要小心。
建议操作流的时候Writer和Reader配对,他们是对字符做处理,而不是字节。
同意你的观点,我一开始读报文头时候是用bufferedreader包装inputstream用readline()来读取报文头的,因为这样方便我判断报文头的结束,结束标识符是“[end header]”,结果当然是linux下流错误截断,我认为是bufferedreader缓冲了报文头结束后的那些数据,所以改成了inputstream。
通过这次也吸取教训了。
fool_leave
2008-03-27
你的结束标示符是什么?
read动作如果返回的是-1,只能表明这次流数据结束,并不是标示你的数据已经到了结尾。换句话说你如果发送出1024个byte,但接收的地方很可能会接收到512个byte之后就读到-1.
这个和操作系统对io缓存的处理也有关系。具体的可能要看IO底层处理了。
即便 while ((tem = socketIn.read(tempbuf, start, buffsize)) != -1) 这句也可能不能正常执行,因为我们并不可以肯定再一次IO流的传输中就可以把全部的数据收到,也可能在还没有读到buffsize大小的数据前就收到了-1。所以应该明确知道自己有多少数据要接收,然后while循环里判断是不是全部收到了,而不是流数据是不是结尾了。
“在linux下一个InputStream不能用字符流和字节流分段读取,因为linux下的字符由于utf8编码会多加几位”
问题不是这个。不论哪个操作系统,都不会在流的数据上加减数据的。用UTF-8编码,也是在收到数据后encode而已。如果读取时设置了reader的编码格式,它会按照这个编码格式来读取word而不是读取byte,不同的编码格式,每个word的长度肯定不一样,这样就会出现将流错误截断的问题了。所以建议你不要将Reader/Writer和IOStream混用。甚至DataInputStream和InputStream在配合使用的时候也要小心。
建议操作流的时候Writer和Reader配对,他们是对字符做处理,而不是字节。
read动作如果返回的是-1,只能表明这次流数据结束,并不是标示你的数据已经到了结尾。换句话说你如果发送出1024个byte,但接收的地方很可能会接收到512个byte之后就读到-1.
这个和操作系统对io缓存的处理也有关系。具体的可能要看IO底层处理了。
即便 while ((tem = socketIn.read(tempbuf, start, buffsize)) != -1) 这句也可能不能正常执行,因为我们并不可以肯定再一次IO流的传输中就可以把全部的数据收到,也可能在还没有读到buffsize大小的数据前就收到了-1。所以应该明确知道自己有多少数据要接收,然后while循环里判断是不是全部收到了,而不是流数据是不是结尾了。
“在linux下一个InputStream不能用字符流和字节流分段读取,因为linux下的字符由于utf8编码会多加几位”
问题不是这个。不论哪个操作系统,都不会在流的数据上加减数据的。用UTF-8编码,也是在收到数据后encode而已。如果读取时设置了reader的编码格式,它会按照这个编码格式来读取word而不是读取byte,不同的编码格式,每个word的长度肯定不一样,这样就会出现将流错误截断的问题了。所以建议你不要将Reader/Writer和IOStream混用。甚至DataInputStream和InputStream在配合使用的时候也要小心。
建议操作流的时候Writer和Reader配对,他们是对字符做处理,而不是字节。
beckrabbit
2008-03-27
fool_leave 写道
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。
还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了
还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了
报文头有结束标识符 while里面有判断逻辑的
fool_leave
2008-03-27
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。
还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了
还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了
发表评论
提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则
- 浏览: 19941 次
- 来自: 厦门

- 详细资料
搜索本博客
最新评论
-
圣斗士星矢的状态模式和观 ...
补充上DyingState类: public class DyingState ...
-- by citi.sh -
ext2的树组件的使用(从底 ...
tree.put("cls", "file");与它对应的树的节点在页面怎么没有 ...
-- by shileishmily -
圣斗士星矢的状态模式和观 ...
java 提供的观察者模式用的是继承。 public class Saiya ...
-- by xufei0110 -
圣斗士星矢的状态模式和观 ...
长见识了~~把动画片和模式联系起来~ 楼主的创意太好了~ 值得学习!!!
-- by zlrzc -
圣斗士星矢的状态模式和观 ...
涨见识了,原来星矢还可以这么玩儿。以后如果记不清Observer,想想星矢就可以 ...
-- by lubezhang






评论排行榜