IO与NIO

概念

NIO是jdk1.4之后引入的。IO是面向流(字符流、字节流)的。NIO是面向缓冲的。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。 参考大佬的详细总结
下面主要是代码的实现

IO

读操作

public  static void read(String path) throws Exception{
		BufferedReader reader = null;
		reader= new BufferedReader(new InputStreamReader(new FileInputStream(new File(path)),"UTF-8"),1024);;
		int result = 0;
		while((result = reader.read()) != -1) {
			String readLine = reader.readLine();
			System.out.println(readLine);
		}
		if(reader != null)
			reader.close();
}

reader.readLine();这行代码是阻塞的,直到有返回值的时候。

写操作

public static void write(String path) throws Exception {
		File file = new File(path);
		if(!file.exists())
			file.createNewFile();
		BufferedWriter bWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"),1024);
		for(int i=0; i<10; i++) {
			String wString = "写入第"+ i+"行";
			if(i!=0)
				bWriter.newLine();
			bWriter.write(wString);
			
		}
		bWriter.flush();
		bWriter.close();
}

NIO

标准输入输出NIO

读操作

public static void readNIO(String pathname) {
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(new File(pathname));
			FileChannel channel = fin.getChannel();

			int capacity = 1000;// 字节
			ByteBuffer cache = ByteBuffer.allocate(capacity);
			int length = -1;
			while ((length = channel.read(cache)) != -1) {

				/*
				 * 注意,读取后,将位置置为0,将limit置为容量, 以备下次读入到字节缓冲中,从0开始存储
				 */
				cache.clear();
				byte[] bytes = cache.array();
				String str = new String(bytes, 0, length);
				System.out.println(str);
			}

			channel.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

写操作

public static void writeNIO(String filename) {
		FileOutputStream fos = null;
		try {

			fos = new FileOutputStream(new File(filename));
			FileChannel channel = fos.getChannel();
			ByteBuffer cache = Charset.forName("utf8").encode("你好你好你好你好你好");
			
			int length = 0;
			while ((length = channel.write(cache)) != 0) {
				System.out.println("写入长度:" + length);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

边读边写

	public static void testReadAndWriteNIO(String pathname, String filename) {
		FileInputStream fin = null;
		FileOutputStream fos = null;
		try {
			fin = new FileInputStream(new File(pathname));
			FileChannel channel = fin.getChannel();

			int capacity = 100;// 字节
			ByteBuffer cache = ByteBuffer.allocate(capacity);
			int length = -1;

			fos = new FileOutputStream(new File(filename));
			FileChannel outchannel = fos.getChannel();

			while ((length = channel.read(cache)) != -1) {

				cache.flip();

				int outlength = 0;
				while ((outlength = outchannel.write(cache)) != 0) {
					System.out.println("读," + length + "写," + outlength);
				}
				cache.clear();

			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

网络编程NIO

选择器

选择器提供选择执行已经就绪的任务的能力,这使得多元I/O成为了可能,就绪执行和多元选择使得单线程能够有效地同时管理多个I/O通道。

某种程度上来说,理解选择器比理解缓冲区和通道类更困难一些和复杂一些,因为涉及了三个主要的类,它们都会同时参与到这整个过程中,这里先将选择器的执行分解为几条细节:

1、创建一个或者多个可选择的通道(SelectableChannel)

2、将这些创建的通道注册到选择器对象中

3、选择器会记住开发者关心的通道,它们也会追踪对应的通道是否已经就绪

4、开发者调用一个选择器对象的select()方法,当方法从阻塞状态返回时,选择键会被更新

5、获取选择键的集合,找到当时已经就绪的通道,通过遍历这些键,开发者可以选择对已就绪的通道要做的操作

用一个线程控制选择器,可以控制多个Channel,也就是控制着多个I/O通道。如下图。
在这里插入图片描述

服务端

public class SelectorServer{
    private static int PORT = 1234;
    
    public static void main(String[] args) throws Exception {
        // 先确定端口号
        int port = PORT;
        if (args != null && args.length > 0){
            port = Integer.parseInt(args[0]);
        }
        // 打开一个ServerSocketChannel
        ServerSocketChannel channel = ServerSocketChannel.open();
        // 获取ServerSocketChannel绑定的Socket
        ServerSocket socket = channel.socket();
        // 设置ServerSocket监听的端口
        socket.bind(new InetSocketAddress(port));
        // 设置ServerSocketChannel为非阻塞模式
        channel.configureBlocking(false);
        // 打开一个选择器
        Selector selector = Selector.open();
        // 将ServerSocketChannel注册到选择器上去并监听accept事件
        channel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 这里会发生阻塞,等待就绪的通道
            int n = selector.select();
            // 没有就绪的通道则什么也不做
            if (n == 0){
                continue;
            }
            // 获取SelectionKeys上已经就绪的通道的集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            // 遍历每一个Key
            while (iterator.hasNext()){
                SelectionKey sk = iterator.next();
                // 通道上是否有可接受的连接
                if (sk.isAcceptable()){
                    ServerSocketChannel ssc1 = (ServerSocketChannel)sk.channel();
                    SocketChannel sc = ssc1.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                }
                // 通道上是否有数据可读
                else if (sk.isReadable()){
                    readDataFromSocket(sk);
                }
                iterator.remove();
            }
        }
    }
    //缓存区
    private static ByteBuffer cache = ByteBuffer.allocate(1024);
    
    // 从通道中读取数据
    protected static void readDataFromSocket(SelectionKey sk) throws Exception{
        SocketChannel sc = (SocketChannel)sk.channel();
        cache.clear();
        while (sc.read(cache) > 0){
            cache.flip();
            while (cache.hasRemaining())
            {
                System.out.print((char)cache.get());
            }
            System.out.println();
            cache.clear();
        }
    }
}

客户端代码

public class SelectorClient {
    private static final String STR = "Hello World!";
    private static final String REMOTE_IP = "127.0.0.1";
    private static final int THREAD_COUNT = 5;
    
    private static class NonBlockingSocketThread extends Thread{
        public void run() {
            try {
                int port = 1234;
                SocketChannel channel  = SocketChannel.open();
                channel.configureBlocking(false);//必须为非阻塞
                channel.connect(new InetSocketAddress(REMOTE_IP, port));
                while (!channel.finishConnect()) {
                    System.out.println("同" + REMOTE_IP + "的连接正在建立,请稍等!");
                    Thread.sleep(10);
                }
                System.out.println("连接已建立,待写入内容至指定ip+端口!时间为" + System.currentTimeMillis());
                String writeStr = STR + this.getName();
                ByteBuffer bb = ByteBuffer.allocate(writeStr.length());
                bb.put(writeStr.getBytes());
                bb.flip(); // 写缓冲区的数据之前一定要先反转(flip)
                channel.write(bb);
                bb.clear();
                channel.close();
            } 
            catch (IOException e) {
                e.printStackTrace();
            } 
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        NonBlockingSocketThread[] nbsts = new NonBlockingSocketThread[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++)
            nbsts[i] = new NonBlockingSocketThread();
        for (int i = 0; i < THREAD_COUNT; i++)
            nbsts[i].start();
        // 一定要join保证线程代码先于sc.close()运行,否则会有AsynchronousCloseException
        for (int i = 0; i < THREAD_COUNT; i++)
            nbsts[i].join();
    }
}

测试

先运行服务端代码,在运行客户端代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值