`
beckrabbit
  • 浏览: 129128 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

阅读更多
最近做了一个报文发送接收解析的客户端,发现了这样的问题:
某程序:

        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) {
...}
分享到:
评论
5 楼 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()  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。
通过这次也吸取教训了。
4 楼 fool_leave 2008-03-27  
久闻javaeye的美名。
不过发现这里都是在解答应用上的问题。而对java本身的讨论很少。
我问的关于socket稳定性的问题连看的人都没有。
是人们水平太高了,还是我的问题太低级了?不解
3 楼 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配对,他们是对字符做处理,而不是字节。
2 楼 beckrabbit 2008-03-27  
fool_leave 写道
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。

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


报文头有结束标识符 while里面有判断逻辑的
1 楼 fool_leave 2008-03-27  
不太建议在流操作的时候将InputStreamReader 和inputStream混用,甚至不建议将不通的Stream混用,除非你特别清楚这些流的封装类都做了什么。

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

相关推荐

    Java字节流与字符流的介绍.pdf

    Java 字节流与字符流的介绍 Java 中的流操作可以分为两种:字节流和字符流。字节流是指以 byte 为单位进行读写操作的流,而字符流是指以 char 为单位进行读写操作的流。 字节流的介绍 字节流的所有读操作都继承自...

    java字节流和字符流

    `InputStream`作为所有字节输入流的基类,提供了读取字节数据的基本方法。例如,`read()`方法用于读取单个字节,`read(byte[] b)`用于读取字节数组。`OutputStream`则是所有字节输出流的基类,它提供写入字节数据的...

    JAVA 字符流与字节流

    在Java编程语言中,输入/输出(I/O)操作是处理数据流的关键部分,而字符流与字节流则是实现这些操作的两种基本方式。理解它们的区别和应用场景对于任何Java开发者来说都是至关重要的。 ### 字节流 字节流是最基本...

    java字节与字符

    ### Java字节与字符 #### 一、字节流与字符流的概念 在Java的I/O包中,根据处理的数据类型不同,可以将流分为两大类:字节流和字符流。 - **字节流**:处理的是8位的数据单元,即字节,主要适用于处理二进制文件...

    字节流字符流

    字节流和字符流的区别在于处理数据的基本单位不同,字节流以8位的字节为单位,而字符流则以16位的Unicode字符为单位。 字节流分为输入流(InputStream)和输出流(OutputStream),主要用来处理二进制数据,如图像...

    字节流字符流练习

    除此之外,InputStreamReader和OutputStreamWriter是字节流与字符流之间的桥梁,它们允许我们在字节流和字符流之间转换,以适应不同编码格式的需求。 在Java IO中,还有一套转换流(Wrapper Stream),即...

    Java IO 字节流 字符流

    InputStream是所有字节输入流的抽象基类,例如FileInputStream,用于从文件中读取字节;OutputStream则是所有字节输出流的基类,如FileOutputStream,用于向文件写入字节。字节流适合处理非文本数据,如图像、音频或...

    socket传输字节和字符串

    接收端则通过Socket的InputStream读取这些字节,再根据需求还原成原始数据。 在“socket传字符”时,我们面临的问题是字符编码。因为字符可能有多种编码形式,如ASCII、UTF-8、GBK等,所以需要确保发送端和接收端...

    Java 字节流、字符流题目.docx

    字节流分为输入流和输出流,Java的字节输入流主要由`InputStream`类及其子类代表,如`FileInputStream`用于从文件中读取字节数据;字节输出流主要由`OutputStream`类及其子类代表,如`FileOutputStream`用于向文件...

    JavaIO实例_字节流_字符流_缓冲流_转换流IODemo

    在Java中,IO流分为两大类:字节流和字符流,每种流又有输入流和输出流之分,分别用于数据的读取和写入。 1. **字节流**: - 字节流处理的是8位的字节数据,是最基本的流类型。Java中的`InputStream`和`...

    java字节流和字符流[借鉴].pdf

    InputStream 是所有字节输入流的父类,它提供了一种从不同来源读取字节数据的方法。例如,如果要从文件中读取数据,可以使用 FileInputStream。InputStream 的主要方法有: 1. 读取单个字节:public int read() ...

    Java IO字符流和字节流

    - **`InputStream`**:用于从源设备读取字节数据。 - **`OutputStream`**:用于向目标设备写入字节数据。 ##### 2. 字节流的基本API - **`public int read()`**:从输入流读取下一个字节。如果流结束,返回-1。 - ...

    java字节流和字符流[整理].pdf

    它们都接受一个InputStream或OutputStream作为参数,然后根据指定的字符集进行转换。 ```java import java.io.*; public class ConvertStreamDemo { public static void main(String[] args) throws IOException ...

    day09_字节流、字符流2

    字节输入流(`InputStream`类)用于读取字节数据。在练习四中,我们可以使用`FileInputStream`配合`read()`方法逐字节读取文件a.txt的内容,并将其输出到控制台。在练习五中,可以使用`read(byte[])`方法读取字节...

    Java基础11-(字节流、字符流)

    通过使用Reader类的子类,我们能够读取存储在不同字符编码格式的文本文件。 最后,Properties类在Java中用于处理配置文件。它能够加载和存储键值对形式的配置信息。load()方法可以用于从输入流中加载属性列表(键和...

    Java字节流数据逐行读取(readLine)

    然而,`readLine()`方法通常与字符流Reader类关联,而不是字节流InputStream类。因此,要使用字节流实现数据逐行读取,我们需要结合`BufferedReader`和`InputStreamReader`。 首先,理解`readLine()`方法。它是`...

    Java 字节流、字符流题目.pdf

    5. 练习五:通过`FileInputStream`读取b.txt内容,一次读取一个字节数组,转换为字符串输出。 6. 练习六:使用字节流`FileInputStream`和`FileOutputStream`实现文件a.png的复制,一次读写一个字节。 二、字符流 ...

    不同字符编码集的文件读取

    ### 不同字符编码集的文件读取 在计算机科学领域,字符编码是将人类可读的文本转换为计算机能够理解的二进制形式的过程。不同的字符编码方式支持不同数量的字符集,这对于处理多语言环境下的文本至关重要。本文将...

    23_IO_第2天(字节流、字符流)_讲义

    因为不同的文本文件可能使用不同的字符编码(如UTF-8,GBK等),所以字符流在传输时会考虑编码问题。常见实现有FileReader和FileWriter,它们可以直接读写文本文件。BufferedReader和BufferedWriter提供缓冲功能,...

Global site tag (gtag.js) - Google Analytics