您的位置:首页 > 其它

io-nio-socket步步为营(一)流基础

2017-10-17 18:10 387 查看
前沿:半路出家,自学io-nio-socket这块有些时日了,反反复复好多次,收获了很多,也记住了很多,但是好像不成系统,这次想彻底整好,不在简单的记住用法,更要用心理解背后的原理。道可道非常道,实践,写入自己的思考。start...

 

一、流基础____________________________________________________________

 

1、流关闭顺序

结论:只需要关闭外层流即可。

思考:因为外层流使用的是Decorator模式,最终还是底层流underlying stream。

 

public static String testStreamColseSequence( String name )throws Exception {
FileInputStream fis = new FileInputStream( name );
// InputStreamReader(fis");默认字符集为UTF-8
Reader reader = new InputStreamReader( fis );
BufferedReader br = new BufferedReader( reader );
String header = br.readLine();
//设置断点,观察varibales:
//br.lock.lock.closed-->reader.lock.closed-->fis.closed=false
br.close();
// 调用fis.read(), 报java.io.IOException: Stream Closed
System.out.println( "fis.read()=" + fis.read() );
return header;
}

   

 

2、io与nio比较

结论:单纯复制byte流方面,io甚至比nio快些,因为从1.4开始io用nio重新实现了,按块读取,但是

nio多个线程复用一个连接的优势,io-socket是无法获得的。

思考:需要操作系统级别的I/O,unix网络编程实践。有待提升

 

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
* 测试数据:2个完全一样的的视频,101M的mp4
* iioCopy=327,nioCopy=422,因为从1.4开始io用nio重写了 ,从FileChannel fc1 = in.getChannel();即可看出端倪
* 面向流 的 I/O系统一次一个字节地处 理数据。NIO按块处理数据比按(流式的)字节处理数据要快得多,但缺少I/O的简单优雅
* @author timeriver.wang
* @date 2012-12-07 9:37:22 AM
*/
public class IOpkNIOCopy {
private static int LEN_8K = 8192;

public static void main( String[] args )
throws Exception {
ioCopy( "e:/work/1/1.MP4", "e:/work/2/1.MP4" );
nioCopy( "e:/work/1/2.MP4", "e:/work/2/2.MP4" );
}

public static void ioCopy( String name1, String name2 )
throws Exception {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream( name1 );
FileOutputStream fos = new FileOutputStream( name2 );
byte[] buf = new byte[LEN_8K];
while ( true ) {
int n = fis.read( buf );
if ( n == -1 ) {
break;
}
fos.write( buf, 0, n );
// 这行代码会使复制的视频变花屏。
// fos.write(99);
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
long time = end - start;
System.out.println( "iioCopy=" + time );
}

public static void nioCopy( String name1, String name2 )
throws Exception {
long start = System.currentTimeMillis();
FileInputStream in = new FileInputStream( name1 );
FileOutputStream out = new FileOutputStream( name2 );
// Since:1.4,java从1.4开始提升性能
// Returns the unique FileChannel object associated with this file input stream.
FileChannel fc1 = in.getChannel();
FileChannel fc2 = out.getChannel();
ByteBuffer bb = ByteBuffer.allocate( LEN_8K );
while ( true ) {
bb.clear();
int n = fc1.read( bb );
if ( n == -1 ) {
break;
}
bb.flip();
fc2.write( bb );
}
fc1.close();
fc2.close();
long end = System.currentTimeMillis();
long time = end - start;
System.out.println( "nioCopy=" + time );
}
}

 

 

3、最基础的InputStream,OutputStream才是最重要的,源代码显示最终都是调用单字节的read()。而且read是抽象的,让子类去实现,非常犀利

 

RandomAccessFile implements DataOutput, DataInput, Closeable

ByteArrayInputStream extends InputStream

 

 

public abstract class InputStream implements Closeable {
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
//    各种read方法,各种上层input流最终都依赖这个read,体味编程之美。
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
int c = read();
}
public long skip(long n) throws IOException {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {}
//    synchronized.
public synchronized void mark(int readlimit) {}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {return false;}
}

 

FileInputStream extends InputStream,重写了read(),skip(),应该是1.4按块读取提供效率。

 

public class FileInputStream extends InputStream{
public native int read() throws IOException;
//不再调用read()了,按块读,不再按byte读取了?
private native int readBytes(byte b[], int off, int len) throws IOException;
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
//也重写父类的skip为natvie方法了,不再调用read()了,
public native long skip(long n) throws IOException;
//since 1.4
public FileChannel getChannel() {
synchronized (this) {FileChannelImpl.open...}
}
}

 

SocketInputStream extends FileInputStream

 

private native int socketRead0(FileDescriptor fd,byte b[], int off, int len,int timeout)

 

4、read()返回[0-255]的int,把有符号的byte数值,转换成了无符号的int数值。可以用来判断是否EOF流结束了与否

,验证代码如下

 

/**
* @author timeriver.wang
* @date 2012-12-10 10:45:17 AM
*/
public class Test {
public static void main( String[] args ) throws Exception{
String filePath = "d:/12.txt";
writeFile(filePath);
readFile( filePath );
}
public static void writeFile(String filePath) throws IOException{
ByteBuffer bb = ByteBuffer.allocate( 20 );
bb.putChar( 'a' );
bb.putInt( -255 );
bb.putShort((short) -255 );
bb.putChar( 'b' );
bb.putChar( 'c' );
FileOutputStream fos = new FileOutputStream(filePath);
fos.write( bb.array());
}

public static void readFile(String filePath) throws IOException{
FileInputStream fis = new FileInputStream(filePath);
ByteBuffer readbuff = ByteBuffer.allocate( 20 );
//写入的byte -1,read读取的是255,即无符号的byte数值。
int i = fis.read();
while(i != -1){
readbuff.put( (byte)i);
System.out.println(i);
i = fis.read();
}
System.out.println(readbuff);
}
}

 

5、InputStream小技巧

 

 

/**
* @author wangnaijiang
* @version 2012-12-30 1:18:12 PM
*/
public class StreamUtils {
/**
* 考虑到网络分包延迟,一直阻塞到填满缓冲区。
*/
public static byte[] read1( InputStream is )throws Exception {
int bytesRead = 0;
byte[] input = new byte[1024];
while ( bytesRead < 1024 ) {
bytesRead += is.read( input, bytesRead, 1024 - bytesRead );
}
return input;
}

/**
* 考虑到网络分包延迟,一直阻塞到填满缓冲区或读取到-1流结束。
*/
public static byte[] read2( InputStream is )throws Exception {
int bytesRead = 0;
byte[] input = new byte[1024];
while ( bytesRead < 1024 ) {
int result = is.read( input, bytesRead, 1024 - bytesRead );
if ( result == -1 )
break;
bytesRead += result;
}
return input;
}
}

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: