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     }
Server 类
技术图片
 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 }
ServerHandler类,业务处理类

  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 }
Client 类

  优点:传统的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 }
Server类
技术图片
 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 }
HandlerExecutorPool 线程池缓冲队列类
技术图片
 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 }
ServerHandler类,业务处理类

  这样改造后,有效的缓解了server端线程数过多时宕机的问题,但是依然没有解决阻塞的问题。依然会出现请求过多时,前台等待超时的问题。

 

以上是关于BIO编程的主要内容,如果未能解决你的问题,请参考以下文章

bio编程示例

java网络编程系列之JavaIO的“前世”:BIO阻塞模型

BIO~~

JAVA BIO 编程

纯Socket(BIO)长链接编程的常见的坑和填坑套路

网络I/O编程2 BIO