Java网络编程之流的详解(未完待续)

原创 2018年04月17日 18:19:18

前言

大部门网络程序做的事情就是接受输入并产生输出。读服务器发送过来的数据与读取本地文件的数据并没有多大的区别,同时服务器将数据发送给客户端与写数据到本地文件也很像。

Java的IO操作基于streams实现的。输入流读数据,输出流写数据。

输出流(Output Streams)

所有输出流的基类是OutputStream
定义的方法如下:

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

基础的方法是write(int b),将参数b的低8位写到输出流中,高24位会被忽略。也就是说b的范围是0~255(二进制11111111是255)。其实就是传了一个无符号字节,因为Java没有无符号字节类型,所以通过int来替代。

但是每次如果只写一个字节很不方便,下面两个write(byte[] data)方法可以一次传输一个字节数组。

其实write(byte [] data)的核心代码还是回调用write(int)方法:

for (int i = 0 ; i < len ; i++) {
    write(b[off + i]);
}

可以把需要发送的数据缓存起来,等到满足一定条件或调用flush方法再发送。Java通过BufferedOutputStreamBufferedWriter来实现。BufferedOutputStream继承自FilterOutputStream,而这个FilterOutputStream是装饰器模式的一个例子,它底层维护了一个OutputStream

不管你觉得有没有必要,调用flush方法就行了。在关闭stream之前,调用flush方法!

常见的关闭流的姿势:

OutputStream out = null;
try {
    out = new FileOutputStream("/tmp/data.txt");
    // work with the output stream...
} catch (IOException ex) {
    System.err.println(ex.getMessage());
} finally {
    if (out != null) {
        try {
            out.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}

Java7引入的更加清爽的姿势:

try (OutputStream out = new FileOutputStream("/tmp/data.txt")) {
    // work with the output stream...
} catch (IOException ex) {
    System.err.println(ex.getMessage());
}

叫做try-with-resources 语句,如果有多个资源呢?

 try (InputStream fis = new FileInputStream(source);
        OutputStream fos = new FileOutputStream(target)){
        byte[] buf = new byte[8192];
        int i;
        while ((i = fis.read(buf)) != -1) {
            fos.write(buf, 0, i);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }

数据流会在 try 执行完毕后自动被关闭,前提是,这些可关闭的资源必须实现 java.lang.AutoCloseable 接口。

输入流(Input Stream)

所有输入流的基类是InputStream
定义的方法如下:

public abstract int read() throws IOException
public int read(byte[] input) throws IOException
public int read(byte[] input, int offset, int length) throws IOException
public long skip(long n) throws IOException
public int available() throws IOException
public void close() throws IOException

基础的是这个无参数的read()方法,从输入流中读取一个字节,并以int类型(0~255)返回,如果读到stream的某位,返回-1。

在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。

每次只读一个字节和每次只写以字节一样不方便,read(byte[] input)方法就出来了。
read(byte[] input)方法尝试填充input数组,注意这里的措辞是尝试,当read的时候遇到了IOException或假设你传入了1024大小的数组,但是只读取了512个字节,还有512个字节正在传输中(你的网络比较慢啊老哥),这个方法就会返回实际读取的字节数。

byte[] input = new byte[1024];
int bytesRead = in.read(input);

考虑到上面所描述的,为了保证你能读到1024字节,将read放到循环里面:

int bytesRead = 0;
int bytesToRead = 1024;
byte[] input = new byte[bytesToRead];
while (bytesRead < bytesToRead) {
    bytesRead += in.read(input, bytesRead, bytesToRead - bytesRead);
}

上面的代码有一个bug,它没有考虑到实际上可能没有1024字节这么多的数据,可能读取了200字节就返回-1了。

int bytesRead = 0;
int bytesToRead = 1024;
byte[] input = new byte[bytesToRead];
while (bytesRead < bytesToRead) {
    int result = in.read(input, bytesRead, bytesToRead - bytesRead);
    if (result == -1) break; // end of stream
    bytesRead += result;
}

调用available()方法可以得知有多少个字节的数据能马上读取而不需要阻塞

int bytesAvailable = in.available();//注意,可能返回0
byte[] input = new byte[bytesAvailable];
int bytesRead = in.read(input, 0, bytesAvailable);

有时,你可能想跳过一些数据,这时可调用skip()方法来达到这个要求。

过滤流(Filter Streams)

Filter有两种方式:filter stream,readers和writers。filter stream主要处理字节数据:比如以二进制的形式来处理字节数据;readers和writers负责处理各种编码的文本。

Filter通过链条的形式组织起来。链路中每个Filter从上个Filter(或stream)上接收数据,自己进行某些处理,然后传递给下一位。

上图展示了一个加密、压缩的文本文件的传输过程。首先传递给TelnetInputStream,然后通过BufferedInputStream来缓存数据以提交传输速度,接下来CipherInputStream解密传递过来的数据并交给GZIPInputStream来解压,最后到了InputStreamReader将解压且解密了的数据转换为Unicode文本的形式。

链接Filter

Filter通过构造函数来接收stream。如下所示:

FileInputStream fin = new FileInputStream("data.txt");
BufferedInputStream bin = new BufferedInputStream(fin);

此时,既可以使用fin的read()方法又可以使用bin的read()方法来从文件data.txt中读取数据。然而,混合调用连接到同一个源文件的不同stream违反了filter stream的约束。一般,应该使用链路上最后一个filter来进行读写操作。

一旦这种链接关系建立起来就无法脱离。

Buffered Streams

BufferedOutputStream存储了需要写的数据,直到buffer满了或stream被flush了,然后它将数据一次性写到底层的output stream。一次写很多数据的速度远超过多次写很少的数据。尤其是在网络环境下。

BufferedInputStream类也有一个名为buf的protected字节数组作为缓冲区。 当调用其中一个流的read()方法时,它首先尝试获取从缓冲区请求的数据。 只有在缓冲区的数据用完时才会从底层源stream中读取数据。因此,它读取尽可能多的数据到缓冲区,而不管是否为马上需要的数据。 在阅读文件时,从本地磁盘读取几百字节的数据几乎和读取一个字节的数据的速度一样快。 因此,缓冲可以大幅度提高性能。

它们的构造函数定义如下:

public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int bufferSize)


public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int bufferSize)

PrintStream

PrintStream是大多数人遇到的第一个filter output stream。因为System.out是一个PrintStream。

PrintStream同样也是通过构造函数来接收stream:

public PrintStream(OutputStream out)
public PrintStream(OutputStream out, boolean autoFlush)

autoFlush默认为false,如果true:byte数组被写入(调用了write(byte buf[])方法);println方法被调用;\n字符被写入 都会触发flush方法。

除了常用的write(),flush()和close()方法。PrintStream还重载了9个print()方法和10个println()方法。

public void print(long l) {
    write(String.valueOf(l));
}
public void print(float f) {
    write(String.valueOf(f));
}

和上面的代码类似,每个print()方法将它的参数转变为string然后以默认的编码写到底层的output stream。

温馨提示:网络编程时最好不要使用PrintStream!

Data Streams

版权声明:欢迎转载,转载请指明出处 https://blog.csdn.net/yjw123456/article/details/79978754

JAVA中的网络编程详解

1、网络编程 网络编程          网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分...
  • fang410103
  • fang410103
  • 2017-03-15 07:44:17
  • 690

java网络编程精解学习笔记(第二章 socket详解)

1 构造socket socket有几个重载的构造方法: Socket() Socket(InetAddress address, int port) //第一个参数表示主机的IP地址 Soc...
  • cg3410
  • cg3410
  • 2017-01-06 20:06:05
  • 271

常见英文专业名词缩写——未完待续

无人驾驶相关: ACC——Adaptive Cruise Control 自适应巡航控制 ADIS——Advanced Driver Information System 先进驾驶员信息系统 A...
  • zhanghm1995
  • zhanghm1995
  • 2017-09-16 19:07:19
  • 505

Java网络编程简介

哈哈!
  • huzanhe
  • huzanhe
  • 2015-08-08 15:33:57
  • 648

Java网络编程精解_孙卫琴PDF

  • 2017年04月03日 22:20
  • 46.5MB
  • 下载

JAVA NIO详解(主要分析在网络编程中的应用)

准备知识: 通常来说,IO操作包括:对硬盘的读写、对socket的读写以及对外设的读写。 以一个IO读取过程为例做简要说明(如图): DMA(直接内存存取)把数据读取到内核空间的缓冲区(读就绪) ...
  • u010653908
  • u010653908
  • 2016-12-05 10:10:02
  • 1134

Java网络编程精解之ServerSocket用法详解二

从上节提到Server端负责接收Client连接,以及与客户端通信,具体代码: while (true) { Socket socket=null; try { socke...
  • u011001916
  • u011001916
  • 2016-05-27 09:03:58
  • 446

java net

  • 2007年07月09日 23:08
  • 1.89MB
  • 下载

Java网络编程精解之ServerSocket用法详解三

3.7  关闭服务器前面介绍的EchoServer服务器都无法关闭自身,只有依靠操作系统来强行终止服务器程序。这种强行终止服务器程序的方式尽管简单方便,但是会 导致服务器中正在执行的任务被突然中断。如...
  • kevin_long
  • kevin_long
  • 2007-06-04 16:54:00
  • 1164

Java网络编程之URL、URLConnection、URLEncoder、URLDecoder

在java的网络编程中让我们获取一个网络地址的志愿
  • fengshizty
  • fengshizty
  • 2014-11-24 16:49:47
  • 1647
收藏助手
不良信息举报
您举报文章:Java网络编程之流的详解(未完待续)
举报原因:
原因补充:

(最多只允许输入30个字)