一、概念
java.io ->bio或jio 阻塞io
nio -> new io 或non-blocking io 非阻塞io
二、BIO
java 1.4以前, io处理主要采用java.net(socket) 和java.io(io) 包中的类进行处理
1、以下是采用单线程方式进行通信
1 package main.io.test; 2 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 import java.util.Scanner; 6 7 public class BioServer { 8 public static void main(String[] args) { 9 try(ServerSocket serverSocket = new ServerSocket(8888)){ 10 System.out.println(String.format("BioServer has started, listing on port:%s, currentThread:%s",serverSocket.getLocalSocketAddress(),Thread.currentThread().getName())); 11 while (true){ 12 Socket clientSocket = serverSocket.accept(); 13 System.out.println(String.format("Connection from ::%s, currentThread:%s",clientSocket.getRemoteSocketAddress(),Thread.currentThread().getName())); 14 try(Scanner inputStream = new Scanner(clientSocket.getInputStream())){ 15 while(true){ 16 System.out.println(String.format("start get input from :%s,currentThread:%s",clientSocket.getRemoteSocketAddress(),Thread.currentThread().getName())); 17 String request = inputStream.nextLine(); 18 System.out.println(String.format("end get input from :%s,currentThread:%s",clientSocket.getRemoteSocketAddress(),Thread.currentThread().getName())); 19 if("quit".equalsIgnoreCase(request)){ 20 break; 21 } 22 System.out.println(String.format("From :%s : %s, currentThread:%s",clientSocket.getRemoteSocketAddress(), request,Thread.currentThread().getName())); 23 String response = "From BioServer Hello " + request + ".\n"; 24 clientSocket.getOutputStream().write(response.getBytes()); 25 } 26 }catch (Exception e){ 27 e.printStackTrace(); 28 } 29 } 30 }catch (Exception e){ 31 e.printStackTrace(); 32 } 33 } 34 }
第二个client连接不上的原因是,主线程一直在等待输入
扫描二维码关注公众号,回复:
6252171 查看本文章
2、采用线程池的方式,来一个client,给其线程进行处理
此处采用线程池而不采用手动new一个线程:
1)创建太多线程,服务器可能会崩掉
2)线程会抢夺cpu执行权,线程上线文的切换会浪费资源
1 package main.io.test; 2 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 8 public class BioThreadPoolServer { 9 public static void main(String[] args) { 10 ExecutorService executorService = Executors.newFixedThreadPool(2); 11 RequestHandler requestHandler = new RequestHandler(); 12 try(ServerSocket serverSocket = new ServerSocket(8888)){ 13 System.out.println(String.format("BioThreadPoolServer has started, listing on port:%s, currentThread:%s",serverSocket.getLocalSocketAddress(),Thread.currentThread().getName())); 14 while (true){ 15 Socket clientSocket = serverSocket.accept(); 16 System.out.println("Connection from :"+ clientSocket.getRemoteSocketAddress() + " currentThread:"+Thread.currentThread().getName()); 17 //采用线程池的方式 18 executorService.submit(new ClientHandler(clientSocket, requestHandler)); 19 } 20 }catch (Exception e){ 21 e.printStackTrace(); 22 } 23 } 24 }
1 package main.io.test; 2 3 import java.net.Socket; 4 import java.util.Scanner; 5 6 public class ClientHandler implements Runnable { 7 Socket clientSocket; 8 RequestHandler requestHandler; 9 public ClientHandler(Socket clientSocket, RequestHandler requestHandler){ 10 this.clientSocket = clientSocket; 11 this.requestHandler = requestHandler; 12 } 13 @Override 14 public void run() { 15 System.out.println("now start ClientHanler run ...., currentThread:"+Thread.currentThread().getName()); 16 try(Scanner inputStream = new Scanner(clientSocket.getInputStream())){ 17 while(true){ 18 String request = inputStream.nextLine(); 19 if("quit".equalsIgnoreCase(request)){ 20 break; 21 } 22 System.out.println(String.format("From :%s : %s, currentThread:%s",clientSocket.getRemoteSocketAddress(), request, Thread.currentThread().getName())); 23 String response = "From BioServer Hello " + request + ".\n" + ", currentThread:"+Thread.currentThread().getName(); 24 clientSocket.getOutputStream().write(response.getBytes()); 25 } 26 }catch (Exception e){ 27 e.printStackTrace(); 28 } 29 } 30 }
1 package main.io.test; 2 3 public class RequestHandler { 4 public void test(){ 5 System.out.println("print test is ok !"); 6 } 7 }
第一个client连接上后就打印出如下信息:
BioThreadPoolServer has started, listing on port:0.0.0.0/0.0.0.0:8888, currentThread:main
Connection from :/0:0:0:0:0:0:0:1:63981 currentThread:main
now start ClientHanler run ...., currentThread:pool-1-thread-1
一直可以连接上server的原因是,处理输入输出的一直是子线程, 主线程仍可以进行连接
若中断第二个client,发现第三个client之前输入的打印了出来
三、NIO
java 1.4之后 java.nio
1 package main.io.test; 2 3 import java.io.IOException; 4 import java.net.InetSocketAddress; 5 import java.nio.ByteBuffer; 6 import java.nio.channels.SelectionKey; 7 import java.nio.channels.Selector; 8 import java.nio.channels.ServerSocketChannel; 9 import java.nio.channels.SocketChannel; 10 import java.util.Iterator; 11 import java.util.Set; 12 13 public class NioServer { 14 public static void main(String[] args) throws IOException { 15 ServerSocketChannel serverChannel = ServerSocketChannel.open(); 16 serverChannel.configureBlocking(false); 17 serverChannel.bind(new InetSocketAddress(9999)); 18 System.out.println(String.format("NIO NioServer has started, listening to port:%s, currentThread:%s",serverChannel.getLocalAddress(), Thread.currentThread().getName())); 19 Selector selector = Selector.open(); 20 serverChannel.register(selector, SelectionKey.OP_ACCEPT); 21 ByteBuffer buffer = ByteBuffer.allocate(1024); 22 RequestHandler requestHandler = new RequestHandler(); 23 while (true){ 24 //唯一一个会阻塞的地方,所有的等待都汇集于此 25 int select = selector.select(); 26 if(select == 0){ 27 continue; 28 } 29 30 Set<SelectionKey> selectionKeys = selector.selectedKeys(); 31 Iterator<SelectionKey> iterator = selectionKeys.iterator(); 32 while (iterator.hasNext()){ 33 SelectionKey key = iterator.next(); 34 if(key.isAcceptable()){ 35 ServerSocketChannel channel = (ServerSocketChannel) key.channel(); 36 SocketChannel clientChannel = channel.accept(); 37 System.out.println(String.format("Connection to from : %s, currentThread:%s",clientChannel.getRemoteAddress(), Thread.currentThread().getName())); 38 clientChannel.configureBlocking(false); 39 clientChannel.register(selector, SelectionKey.OP_READ); 40 } 41 42 if(key.isReadable()){ 43 SocketChannel channel = (SocketChannel) key.channel(); 44 channel.read(buffer); 45 String request = new String(buffer.array()).trim(); 46 System.out.println(String.format("From %s : request:%s, currentThread:%s", channel.getRemoteAddress(), request, Thread.currentThread().getName())); 47 String response = requestHandler.test(request); 48 channel.write(ByteBuffer.wrap(response.getBytes())); 49 50 } 51 iterator.remove(); 52 } 53 } 54 } 55 }
当然我们不必写NIO代码,目前Netty就是对NIO很好的封装和优化。