BIO编程
Posted liyasong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BIO编程相关的知识,希望对你有一定的参考价值。
在实际的工作开发中,传统的模型有client/service模型。client端和service端要进行通信的话,有一种套接字的方式。传统的socket编程,包含一个socket服务端和一到多个socket客户端。在连接过程中,sokcetServer 绑定一个端口进行监听。client端通过ip地址和端口号对服务进行访问。在服务进入到service端后,service端新建一个线程。线程对请求进行相应的处理,处理完毕后,将处理结果返回给client端。在处理过程中,连接client阻塞。
代码实现:
server端代码:
1 import java.io.IOException; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 5 6 public class Server { 7 8 final static int PROT = 8765; 9 10 public static void main(String[] args) { 11 12 ServerSocket server = null; 13 try { 14 server = new ServerSocket(PROT); 15 System.out.println(" server start .. "); 16 while (true) { 17 //进行阻塞 18 Socket socket = server.accept(); 19 //新建一个线程执行客户端的任务 20 new Thread(new ServerHandler(socket)).start(); 21 } 22 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } finally { 26 if(server != null){ 27 try { 28 server.close(); 29 } catch (IOException e) { 30 e.printStackTrace(); 31 } 32 } 33 server = null; 34 } 35 36 37 38 }
1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStreamReader; 4 import java.io.PrintWriter; 5 import java.net.Socket; 6 7 public class ServerHandler implements Runnable{ 8 9 private Socket socket ; 10 11 public ServerHandler(Socket socket){ 12 this.socket = socket; 13 } 14 15 @Override 16 public void run() { 17 BufferedReader in = null; 18 PrintWriter out = null; 19 try { 20 in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); 21 out = new PrintWriter(this.socket.getOutputStream(), true); 22 String body = null; 23 while(true){ 24 body = in.readLine(); 25 if(body == null) break; 26 System.out.println("Server :" + body); 27 out.println("服务器端回响的应数据."); 28 } 29 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } finally { 33 if(in != null){ 34 try { 35 in.close(); 36 } catch (IOException e) { 37 e.printStackTrace(); 38 } 39 } 40 if(out != null){ 41 try { 42 out.close(); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 } 47 if(socket != null){ 48 try { 49 socket.close(); 50 } catch (IOException e) { 51 e.printStackTrace(); 52 } 53 } 54 socket = null; 55 } 56 57 58 } 59 60 }
client端代码:
1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStreamReader; 4 import java.io.PrintWriter; 5 import java.net.Socket; 6 7 public class Client { 8 9 final static String ADDRESS = "127.0.0.1"; 10 final static int PORT = 8765; 11 12 public static void main(String[] args) { 13 14 Socket socket = null; 15 BufferedReader in = null; 16 PrintWriter out = null; 17 18 try { 19 socket = new Socket(ADDRESS, PORT); 20 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 21 out = new PrintWriter(socket.getOutputStream(), true); 22 23 //向服务器端发送数据 24 out.println("接收到客户端的请求数据..."); 25 out.println("接收到客户端的请求数据1111..."); 26 String response = in.readLine(); 27 System.out.println("Client: " + response); 28 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 if(in != null){ 33 try { 34 in.close(); 35 } catch (IOException e) { 36 e.printStackTrace(); 37 } 38 } 39 if(out != null){ 40 try { 41 out.close(); 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } 45 } 46 if(socket != null){ 47 try { 48 socket.close(); 49 } catch (IOException e) { 50 e.printStackTrace(); 51 } 52 } 53 socket = null; 54 } 55 } 56 }
优点:传统的BIO模型进行通信,代码可读性比较强,理解容易。运行中,server端通过主线程监听accpet()方法阻塞式获取server的消息。
缺点:该模型最大的问题是,每一个client端访问都对应一个后台的线程,使得系统不具备弹性伸缩能力。一旦client请求过多,线程数会迅速膨胀,系统性能会急剧下降,导致堆栈溢出,进程宕机等问题的发生。
为了改进线程数过多,导致系统宕机的问题,同时也为了解决创建线程的消耗问题。后来又演进出了一种通过线程池或者消息队列实现1个或者多个线程处理N个客户端的模型,由于它的底层通信机制依然使用同步阻塞IO,所以被称为 “伪异步”。其原理就是在原来的service上新增一个线程池,将处理业务的线程通过线程池进行管理。如果线程数量到达上限时,将请求先存入queue中进行缓冲。具体代码如下:
client端代码不变。
server端:
1 import java.io.BufferedReader; 2 import java.io.PrintWriter; 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 6 public class Server { 7 8 final static int PORT = 8765; 9 10 public static void main(String[] args) { 11 ServerSocket server = null; 12 BufferedReader in = null; 13 PrintWriter out = null; 14 try { 15 server = new ServerSocket(PORT); 16 System.out.println("server start"); 17 Socket socket = null; 18 HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 50); 19 while(true){ 20 socket = server.accept(); 21 22 executorPool.execute(new ServerHandler(socket)); 23 24 } 25 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } finally { 29 if(in != null){ 30 try { 31 in.close(); 32 } catch (Exception e1) { 33 e1.printStackTrace(); 34 } 35 } 36 if(out != null){ 37 try { 38 out.close(); 39 } catch (Exception e2) { 40 e2.printStackTrace(); 41 } 42 } 43 if(server != null){ 44 try { 45 server.close(); 46 } catch (Exception e3) { 47 e3.printStackTrace(); 48 } 49 } 50 server = null; 51 } 52 53 54 55 } 56 57 58 }
1 import java.util.concurrent.ArrayBlockingQueue; 2 import java.util.concurrent.ThreadPoolExecutor; 3 import java.util.concurrent.TimeUnit; 4 5 6 public class HandlerExecutorPool { 7 private ArrayBlockingQueue queue ; 8 private ThreadPoolExecutor executor; 9 public HandlerExecutorPool(int maxPoolSize, int queueSize){ 10 queue = new ArrayBlockingQueue<Runnable>(queueSize); 11 this.executor = new ThreadPoolExecutor( 12 5, 13 maxPoolSize, 14 120L, 15 TimeUnit.SECONDS, 16 queue); 17 } 18 19 public void execute(Runnable task){ 20 System.out.println("corePoolSize== "+executor.getCorePoolSize()); 21 System.out.println("queuesize=="+queue.size()); 22 this.executor.execute(task); 23 24 System.out.println("当前运行线程== "+executor.getLargestPoolSize()); 25 System.out.println("queuesize=="+queue.size()); 26 } 27 28 29 30 }
1 import java.io.BufferedReader; 2 import java.io.InputStreamReader; 3 import java.io.PrintWriter; 4 import java.net.Socket; 5 import java.util.concurrent.TimeUnit; 6 7 public class ServerHandler implements Runnable { 8 9 private Socket socket; 10 public ServerHandler (Socket socket){ 11 this.socket = socket; 12 } 13 14 @Override 15 public void run() { 16 BufferedReader in = null; 17 PrintWriter out = null; 18 try { 19 Thread.currentThread().sleep(1000); 20 in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); 21 out = new PrintWriter(this.socket.getOutputStream(), true); 22 String body = null; 23 while(true){ 24 body = in.readLine(); 25 if(body == null) break; 26 System.out.println("Server:" + body); 27 out.println("Server response"); 28 } 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 if(in != null){ 33 try { 34 in.close(); 35 } catch (Exception e1) { 36 e1.printStackTrace(); 37 } 38 } 39 if(out != null){ 40 try { 41 out.close(); 42 } catch (Exception e2) { 43 e2.printStackTrace(); 44 } 45 } 46 if(socket != null){ 47 try { 48 socket.close(); 49 } catch (Exception e3) { 50 e3.printStackTrace(); 51 } 52 } 53 socket = null; 54 } 55 56 57 } 58 59 }
这样改造后,有效的缓解了server端线程数过多时宕机的问题,但是依然没有解决阻塞的问题。依然会出现请求过多时,前台等待超时的问题。
以上是关于BIO编程的主要内容,如果未能解决你的问题,请参考以下文章