论坛首页 Java企业应用论坛

Windows与Linux下InputStream读取字节与字符的不同

浏览 5496 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-03-27  
最近做了一个报文发送接收解析的客户端,发现了这样的问题:
某程序:

        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) {
...}
   发表时间:2008-03-27  
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。

还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了
0 请登录后投票
   发表时间:2008-03-27  
fool_leave 写道
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。

还有你的报文头不是定长的?至少也有一个长度位吧。
while ((headchar = isr.read()) != -1)太不安全了


报文头有结束标识符 while里面有判断逻辑的
0 请登录后投票
   发表时间: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配对,他们是对字符做处理,而不是字节。
0 请登录后投票
   发表时间: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()  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。
通过这次也吸取教训了。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics