Java网络编程-流

概述

Java的输入输出I/O的组织与其他大多数语言都不一样。

I/O基于stream之上,输入流读取数据,输出流写入数据。

还有过滤器(filter),阅读器(reader)和书写器(writer)。

过滤器(filter)能够改写数据,转换格式。

阅读器(reader)与书写器(writer),能够改写字符,文本而不是字节。

流是同步的,即一次只能处理一个事务,容易造成阻塞,但是Java支持通道和缓冲区技术,即非阻塞I/O技术,通道与缓冲区都依赖于流。

输出流

基本输出类:java.io.OutputStream

向某种介质写入数据。

Fileoutputstream->写入文件

Telnetoutputstream->写入网络连接

Bytearraryoutputstream->写入字节数组()可拓展

注意:Telnetoutputstream类被隐藏在了sun包中。

Outputstream的基本方法:write(int b)b为0~255的整数。且b应该为无符号字节,因为java没有无符号类型数据,只能使用int(4字节)代替,实际上只会填入最低一个字节,即只填入了8个二进制数字。这里的int实际上被强制类型转换成byte。

Fileoutputstream 必须使用纯java实现此方法才能将字节写入数组。

Bytearraryoutputstream 则需要原生代码实现。

流也存在缓存机制,bufferedoutputstream或是bufferedwriter实现的。写入完成后flush()刷新十分重要。

Flush作用:

 

//以下代码是java7之前的释放模式
OutputStream out = null;//流变量须try前声明
try {
       out= new FileOutputStream("/tem/data.txt");
       //流变量须try内初始化
       //*关闭流之前要进行缓冲区清空(不是丢弃),即刷新(输出)
 
} catch(IOException ex) {
       System.err.println(ex.getMessage());
}
finally { //在finally块内关闭流
       if(out!=null){ //关闭前需要判断是否没有输入输出
              try{
                     out.close();//关闭流 如果还有输出输入则抛出异常
              }catch(IOException ex) {
                     //忽略
              }
       }
}//长时间未能关闭流,会泄露文件句柄,网络端口,其他资源
//以下代码是java7之后的清理模块
try(OutputStream out = out = newFileOutputStream("/tem/data.txt");) {
       //处理流
} catch(IOException ex) {
       System.err.println(ex.getMessage());
}

输入流

基本输出类:java.io.IutputStream

向某种介质写入数据。

FileInputstream->读取数据

TelnetInputstream->读取网络数据

BytearraryInputstream->读取数据字节到数组()可拓展

注意:TelnetInputstream类被隐藏在了sun包中。

InputStream的基本方法是read( )无参,读入流结束则返回-1,无论是read()还是write()都较为缓慢,容易阻塞,尽量安排写入或读取工作在后面或是单独一个线程.

byte[] input = new byte[10];
for(int i=0;i<input.Lengh;i++){
       intb=in.read();//每次只能读入一个字节
       if(b==-1)break;//-1为流结束标志
       input[i]=(byte)b;//返回int类型需要进行类型转换
       }
}
//也可以进行符号处理
int i=b>=0?b:256+b;
//因为返回的是有符号的-128到127 而实际需要的是无符号的

介绍两个重载read():

Read(byte[] input)//尝试填满指定数组

Read(byte[] input int offset, int length)//尝试从offset开始长度为length的数组填充

这两个数组均为尝试,实际情况很是复杂,不能保证传输,所以可以使用下面的方法:

</pre><pre name="code" class="java">//确保传输准确无误
int bytesRead = 0;//已经传输
int bytesToRead = 1024;//应该传输
byte[] input = new byte[bytesToRead];
while(bytesRead<bytesToRead){
       bytesRead+=in.read(input,bytesRead,bytesToRead-bytesRead);
}

标记与重置

这个标记与重置,主要用于备份与重新读取数据。

Mark()标记

Reset()将流重置到标记之前的位置

标记:1.只能有一个标记点 2.readAheadLimit确定标记距离(不能无限制重置距离)

有些类输入输出流不支持标记,查看能否进行标记重置,可通过marksupposed()进行查看返回,如果返回true则可以进行标记与重置。

BufferedInputStream与ByteArrayInputStream 这两个类始终支持标记与重置。

缓冲流

顾名思义就是缓冲区的流,缓冲区的进行写入与读取操作的流。

从缓冲区写入底层输出流:一次多字节,与多次少字节相比,同样字节量,一次较快。

Read()方法调用时会先到缓冲区进行读取操作,如果缓冲区为空,read()会尽可能多的读取数据存入缓冲区,而不是马上就使用它所需的数据。缓冲区的作用在于,从底层流读取几百字节与读取一个字节效率一样,但是放入缓冲区就会大大提高效率。

输入流缓冲区大小默认为2048,而输出流默认大小为512.

PrintStream

过滤输出流,而system.out就是一个过滤输出流,大家接触的第一个过滤输出流。

Print() 会将参数转换为字符串,使用默认编码写入底层输出流。

PrintStream是有害的,本书作者讲到。

警告:PrintStream是有害的,网络程序员应当像躲避瘟疫一样避开它!!!!(作者(译者)原话)

第一个问题是println()的输出是与平台有关的。取决于运行代码的机器,各行有时用换行符分隔,有时则用回车符或者回车/换行对来分隔。写入控制台时这不会产生问题,但对于编写必须遵循明确协议的网络客户端和服务器而言,这却是灾难。大多数网络协议(如HTTP和Gnutela)明确指定行应当以回车/换行对结束。使用println()写出的程序很有可能可以在windows上正常工作,但在UNIX和Mac上无法工作。虽然许多服务器和客户端能够“宽容”地接受而且能处理不正确的行结束符,但偶尔也有例外。

第二个问题是PrintStream假定使用所在平台的默认编码方式。不过,这种编码方式可能不是服务器或客户端所期望的。例如,一个接收XML文件的Web浏览器希望文件以UTF-8或UTF16方式编码,除非服务器另行要求。不过,一个使用PrintStream的Web服务器可能会从一个美国本地化环境的windows系统发送CP1251编码的文件,或者从日本本地化环境的系统发送SJIS编码的文件,而不管客户端是否期望或理解这些编码方式。PrintStream不提供任何改变默认编码的机制。这个问题可以通过使用相关的PrintWriter类来修补。但是其他问题依旧。

第三个问题是PrintStream吞掉了所有异常。注意PrintStream中5个标准OutputStream方法的声明没有平常的throws IOException声明:

public abstract void write(int b)
public void write(byte[] data)
public void write(byte[] data,intoffset,int length)
public void void flush()
public void close()

 

实际上,PrintStream要依靠一个过时的不充分的错误标志。如果底层流抛出一个异常,就会设置这个内部错误标志。要由程序员使用checkError()方法来检查这个标志的值:

public booleean checkError()

要对PrintStream完成任何错误检查,代码必须显式地检查每一个调用。此外,一旦出现在错误,就没有办法重置这个标志再进行进一步错误检测。也没有关于这个错误的更多信息。简而言之,PrintStream提供的错误通知对于不可靠的网络连接来说还远远不够。

数据流

二进制格式读写Java基本数据类型和字符串。

未经允许不得转载:JX BLOG » Java网络编程-流

赞 (0)

评论 0

评论前必须登录!

登陆 注册