• BIO与NIO


    BIO与NIO

    1.传统BIO

    (1)特点

    1. 面向数据流
    2. 阻塞式传输
    3. 一个客户端对应一个线程
    4. 在客户机增多的情况下,线程资源随之增多,会造成cpu资源枯竭

    (2)需求

    ​ 客户机向服务器输出字符串,逐一在服务器器上打印显示。类似一个简陋的聊天室功能。

    (3)代码示例

    1. 服务器程序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;
      			}
      		}
      
      	}
      
      }
      
      
    2. 服务器处理客户机进程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();
      				}
      				
      			}
      		}
      		
      
      	}
      
      }
      
      
    3. 客户端程序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下的本地文件传输

    1. 内存映射下的缓冲通道

      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());
      		
      	}
      
      }
      
      
    2. 物理映射下的缓冲通道

      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下的网络传输

    1. 阻塞式

      • 服务端程序

        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();
        
        	}
        
        }
        
        
    2. 非阻塞式

      • 服务端程序

        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();
        		}
        		
        	}
        		
        
        
        }
        
        
  • 相关阅读:
    并发编程bug的源头
    lambda表达式
    如何学习并发编程
    开篇词
    试述软件的概念和特点?软件复用的含义?构件包括哪些?
    软件生存周期及其模型是什么?
    一台客户端有三百个客户与三百个客户端有三百个客户对服务器施压,有什么区别?
    在搜索引擎中输入汉字就可以解析到对应的域名,请问如何用LoadRunner进行测试。
    给你一个网站,你如何测试?
    你在测试中发现了一个bug,但是开发经理认为这不是一个bug,你应该怎样解决?
  • 原文地址:https://www.cnblogs.com/TimerHotel/p/java_nio.html
Copyright © 2020-2023  润新知