BIO与NIO
1.传统BIO
(1)特点
- 面向数据流
- 阻塞式传输
- 一个客户端对应一个线程
- 在客户机增多的情况下,线程资源随之增多,会造成cpu资源枯竭
(2)需求
客户机向服务器输出字符串,逐一在服务器器上打印显示。类似一个简陋的聊天室功能。
(3)代码示例
-
服务器程序TimeServer.java
package com.xm.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class TimeServer { public static void main(String[] args) throws IOException { int port = 8080; if(args != null && args.length>0) { try { port = Integer.parseInt(args[0]); } catch (Exception e) { } } ServerSocket server = null; try { server = new ServerSocket(port); System.out.println("开启服务器:"+server.getLocalSocketAddress()); Socket socket = null; while(true) { socket = server.accept(); new Thread(new TimeServerHandle(socket)).start(); } } finally { if(server != null) { System.out.println("服务器已关闭!"); server.close(); server = null; } } } }
-
服务器处理客户机进程TimeServerHandle.java
package com.xm.bio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Date; public class TimeServerHandle implements Runnable { private Socket socket; public TimeServerHandle(Socket socket) { this.socket = socket; } @Override public void run() { System.out.println(socket.getInetAddress().toString()+"客户机已连接"); BufferedReader in = null; PrintWriter out = null; BufferedReader wt = null; try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); wt = new BufferedReader(new InputStreamReader(System.in)); while(true) { System.out.println(in.readLine()); } } catch (Exception e) { } finally { if(in != null) { try { in.close(); in = null; } catch (IOException e) { e.printStackTrace(); } } if(out != null) { out.close(); out = null; } if(socket != null) { try { socket.close(); socket = null; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
-
客户端程序TimeClient.java
package com.xm.bio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class TimeClient { public static void main(String[] args) { int port = 8080; String host = "127.0.0.1"; Socket socket = null; BufferedReader in = null; BufferedReader wt = null; PrintWriter out = null; try { socket = new Socket(host, port); wt = new BufferedReader(new InputStreamReader(System.in)); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); String body = null; while(true) { String str = wt.readLine(); out.println(str); } } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { wt.close(); in.close(); out.close(); socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
2.NIO
(1)NIO特点
1.面向缓冲区
2.传输方式为管道传输
3.非阻塞
4.支持大并发下的io处理
(2)NIO下的本地文件传输
-
内存映射下的缓冲通道
package com.xm.nio; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; import org.junit.jupiter.api.Test; public class NIOFileDemo { /** * 1.通过流获取通道 */ @Test public void test1() { Instant begin = Instant.now(); //1.定义文件流 FileInputStream fis = null; FileOutputStream fos = null; //2.获取通道 FileChannel inChannel = null; FileChannel outChannel = null; try { fis = new FileInputStream("1.jpg"); fos = new FileOutputStream("2.jpg"); inChannel = fis.getChannel(); outChannel = fos.getChannel(); //3.定义缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //4.读取数据到缓冲区,再从缓冲区写入到文件 while(inChannel.read(buffer) != -1) { //切换到读模式 buffer.flip(); //写操作到管道 outChannel.write(buffer); //清空buffer buffer.clear(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { //5.关闭通道和流 if(inChannel != null) { try { inChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(outChannel != null) { try { outChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(fis != null) { try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(fos != null) { try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Instant end = begin.plus(Duration.ofSeconds(10)); System.out.println("Difference in milliseconds : " + Duration.between(begin, end).toMillis()); } /** * 通过文件获取管道 */ @Test public void test2() { Instant begin = Instant.now(); FileChannel inChannel =null; FileChannel outChannel = null; try { inChannel =FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); /** * StandardOpenOption.CREATE与StandardOpenOption.CREATE_NEW的区别 * 1.StandardOpenOption.CREATE:无则创建,有则覆盖 * 2.StandardOpenOption.CREATE_NEW:无则创建,有则报错 */ outChannel =FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); //3.定义缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //4.读取数据到缓冲区,再从缓冲区写入到文件 while(inChannel.read(buffer) != -1) { //切换到读模式 buffer.flip(); //写操作到管道 outChannel.write(buffer); //清空buffer buffer.clear(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { //5.关闭通道和流 if(inChannel != null) { try { inChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(outChannel != null) { try { outChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Instant end = begin.plus(Duration.ofSeconds(10)); System.out.println("Difference in milliseconds : " + Duration.between(begin, end).toMillis()); } }
-
物理映射下的缓冲通道
package com.xm.nio; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; import org.junit.Test; public class NIOFileDemo2 { /** * 使用直接缓冲区传输 */ @Test public void test1() { Instant begin = Instant.now(); FileChannel inChannel = null; FileChannel outChannel = null; try { //1.开启通道 inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE,StandardOpenOption.READ); //2.定义物理缓冲区 MappedByteBuffer inBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); //3.缓冲区读写操作 byte[] dst = new byte[inBuffer.limit()]; inBuffer.get(dst); outBuffer.put(dst); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { //4.关闭通道 if(null != inChannel) { try { inChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(null != outChannel) { try { outChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Instant end = begin.plus(Duration.ofSeconds(10)); System.out.println("Difference in milliseconds : " + Duration.between(begin, end).toMillis()); } /** * 通道之间的传输 */ @Test public void test2() { Instant begin = Instant.now(); FileChannel inChannel = null; FileChannel outChannel = null; //获取通道 try { inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); //通道间传输 //1.to操作 //inChannel.transferTo(0, inChannel.size(), outChannel); //2.from操作 outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //4.关闭通道 if(null != inChannel) { try { inChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(null != outChannel) { try { outChannel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Instant end = begin.plus(Duration.ofSeconds(10)); System.out.println("Difference in milliseconds : " + Duration.between(begin, end).toMillis()); } }
(3)NIO下的网络传输
-
阻塞式
-
服务端程序
package com.xm.nio.block; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Scanner; public class NIOServer { public static void main(String[] args) throws IOException { int port = 8989; //1.获取通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //2.绑定端口号 serverChannel.bind(new InetSocketAddress(port)); //3.获取客户端连接 SocketChannel socketChannel = serverChannel.accept(); //定义文件传输通道 FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); ByteBuffer buffer = ByteBuffer.allocate(1024); while(socketChannel.read(buffer)!=-1) { buffer.flip(); outChannel.write(buffer); buffer.clear(); } outChannel.close(); socketChannel.close(); } }
-
客户端程序
package com.xm.nio.block; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SocketChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class NIOClient { public static void main(String[] args) throws IOException { int port = 8989; //1.获取通道 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", port)); //2.获取文件通道 FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); inChannel.transferTo(0, inChannel.size(), socketChannel); //3.关闭通道 inChannel.close(); socketChannel.close(); } }
-
-
非阻塞式
-
服务端程序
package com.xm.nio.noblock; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class NIOServer { public static void main(String[] args) throws IOException { int port = 8989; //1.开启通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //2.绑定端口号 serverChannel.bind(new InetSocketAddress(port)); //3.设置非阻塞 serverChannel.configureBlocking(false); //4.开启选择器 Selector selector = Selector.open(); //5.注册连接监听 serverChannel.register(selector, SelectionKey.OP_ACCEPT); List<SocketChannel> channels = new ArrayList<>(); while(selector.select() > 0) { Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while(keys.hasNext()) { SelectionKey key = keys.next(); if(key.isAcceptable()) { SocketChannel socketChannel = serverChannel.accept(); channels.add(socketChannel); System.out.println("客户端连接成功:"+socketChannel.getLocalAddress()+" hashcode:"+socketChannel.hashCode()); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } else if(key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer dst = ByteBuffer.allocate(1024); int len; while(-1 != (len=socketChannel.read(dst))) { dst.flip(); System.out.println(new String(dst.array(),0,len)); /*for(SocketChannel sChannel:channels) { if(sChannel != socketChannel) { dst.flip(); sChannel.write(dst); } }*/ dst.clear(); } } } keys.remove(); } } }
-
客户端
package com.xm.nio.noblock; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Iterator; import java.util.Scanner; public class NIOClient{ SocketChannel socketChannel; public NIOClient() throws IOException { int port = 8989; //1.获取通道 socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", port)); //2.设置异步非阻塞 socketChannel.configureBlocking(false); //2.获取文件通道 FileChannel inChannel = FileChannel.open(Paths.get("1.md"), StandardOpenOption.READ); inChannel.transferTo(0, inChannel.size(), socketChannel); //3.关闭通道 inChannel.close(); socketChannel.close(); } public static void main(String[] args) { try { new NIOClient(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
-