zoukankan      html  css  js  c++  java
  • 基于NIO和BIO的两种服务器对比

    基于BIO的服务器,服务端可能要同时保持几百万个HTTP连接,而这些连接并不是每时每刻都在传输数据,所以这种情况不适合使用BIO的服务器;而且需要保证共享资源的同步与安全,这个实现起来相对复杂。这时候就是基于NIO的服务器出场的时候了。

    先来比较下两种服务器的代码

    1.NIO实现

     1 public class SelectorServer
     2 {
     3     private static int PORT = 1234;
     4     
     5     public static void main(String[] args) throws Exception
     6     {
     7         // 先确定端口号
     8         int port = PORT;
     9         if (args != null && args.length > 0)
    10         {
    11             port = Integer.parseInt(args[0]);
    12         }
    13         // 打开一个ServerSocketChannel
    14         ServerSocketChannel ssc = ServerSocketChannel.open();
    15         // 获取ServerSocketChannel绑定的Socket
    16         ServerSocket ss = ssc.socket();
    17         // 设置ServerSocket监听的端口
    18         ss.bind(new InetSocketAddress(port));
    19         // 设置ServerSocketChannel为非阻塞模式
    20         ssc.configureBlocking(false);
    21         // 打开一个选择器
    22         Selector selector = Selector.open();
    23         // 将ServerSocketChannel注册到选择器上去并监听accept事件
    24         ssc.register(selector, SelectionKey.OP_ACCEPT);
    25         while (true)
    26         {
    27             // 这里会发生阻塞,等待就绪的通道
    28             int n = selector.select();
    29             // 没有就绪的通道则什么也不做
    30             if (n == 0)
    31             {
    32                 continue;
    33             }
    34             // 获取SelectionKeys上已经就绪的通道的集合
    35             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
    36             // 遍历每一个Key
    37             while (iterator.hasNext())
    38             {
    39                 SelectionKey sk = iterator.next();
    40                 // 通道上是否有可接受的连接
    41                 if (sk.isAcceptable())
    42                 {
    43                     ServerSocketChannel ssc1 = (ServerSocketChannel)sk.channel();
    44                     SocketChannel sc = ssc1.accept();
    45                     sc.configureBlocking(false);
    46                     sc.register(selector, SelectionKey.OP_READ);
    47                 }
    48                 // 通道上是否有数据可读
    49                 else if (sk.isReadable())
    50                 {
    51                     readDataFromSocket(sk);
    52                 }
    53                 iterator.remove();
    54             }
    55         }
    56     }
    57     
    58     private static ByteBuffer bb = ByteBuffer.allocate(1024);
    59     
    60     // 从通道中读取数据
    61     protected static void readDataFromSocket(SelectionKey sk) throws Exception
    62     {
    63         SocketChannel sc = (SocketChannel)sk.channel();
    64         bb.clear();
    65         while (sc.read(bb) > 0)
    66         {
    67             bb.flip();
    68             while (bb.hasRemaining())
    69             {
    70                 System.out.print((char)bb.get());
    71             }
    72             System.out.println();
    73             bb.clear();
    74         }
    75     }
    76 }

    2.BIO 多线程实现

     1 package com.defonds.socket.begin;  
     2   
     3 import java.io.BufferedReader;  
     4 import java.io.DataInputStream;  
     5 import java.io.DataOutputStream;  
     6 import java.io.InputStreamReader;  
     7 import java.net.ServerSocket;  
     8 import java.net.Socket;  
     9   
    10 public class Server {  
    11     public static final int PORT = 12345;//监听的端口号     
    12       
    13     public static void main(String[] args) {    
    14         System.out.println("服务器启动...
    ");    
    15         Server server = new Server();    
    16         server.init();    
    17     }    
    18     
    19     public void init() {    
    20         try {    
    21             ServerSocket serverSocket = new ServerSocket(PORT);    
    22             while (true) {    
    23                 // 一旦有堵塞, 则表示服务器与客户端获得了连接    
    24                 Socket client = serverSocket.accept();    
    25                 // 处理这次连接    
    26                 new HandlerThread(client);    
    27             }    
    28         } catch (Exception e) {    
    29             System.out.println("服务器异常: " + e.getMessage());    
    30         }    
    31     }    
    32     
    33     private class HandlerThread implements Runnable {    
    34         private Socket socket;    
    35         public HandlerThread(Socket client) {    
    36             socket = client;    
    37             new Thread(this).start();    
    38         }    
    39     
    40         public void run() {    
    41             try {    
    42                 // 读取客户端数据    
    43                 DataInputStream input = new DataInputStream(socket.getInputStream());  
    44                 String clientInputStr = input.readUTF();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException  
    45                 // 处理客户端数据    
    46                 System.out.println("客户端发过来的内容:" + clientInputStr);    
    47     
    48                 // 向客户端回复信息    
    49                 DataOutputStream out = new DataOutputStream(socket.getOutputStream());    
    50                 System.out.print("请输入:	");    
    51                 // 发送键盘输入的一行    
    52                 String s = new BufferedReader(new InputStreamReader(System.in)).readLine();    
    53                 out.writeUTF(s);    
    54                   
    55                 out.close();    
    56                 input.close();    
    57             } catch (Exception e) {    
    58                 System.out.println("服务器 run 异常: " + e.getMessage());    
    59             } finally {    
    60                 if (socket != null) {    
    61                     try {    
    62                         socket.close();    
    63                     } catch (Exception e) {    
    64                         socket = null;    
    65                         System.out.println("服务端 finally 异常:" + e.getMessage());    
    66                     }    
    67                 }    
    68             }   
    69         }    
    70     }    
    71 }    

    基于NIO的服务器相当于把老版本多线程服务器中的read和accept的阻塞时间,都统一集中到了selector.select()里面。当有socket数据过来的时候,selector会选择与当前socket数据对应的一个且已经在其内部注册的channel来执行对应的方法。相当于在老版本多线程服务器中抽象出了一层,这一层就是select方法,这个方法负责接收"任务"以及分发"任务"。

    第一种,开多线程可以去接多个请求,防止一个线程的read卡住,无法接收其他客户端的请求。

    第二种,把服务提供者全部托管给selector,当有消费任务到来的时候,选择与当前任务适配的服务提供者去提供(而真正的读写操作的阻塞是使用CPU的,真正在"干活",而且这个过程非常快,属于memory copy,带宽通常在1GB/s级别以上,可以理解为基本不耗时)

  • 相关阅读:
    使用cout进行格式化
    20175324 《Java程序设计》第3周学习总结
    20175324第二周学习总结
    第三周学习总结
    JAVA第二周学习总结
    20175330第一周学习总结。
    20175330第一周学习总结
    指针
    数组总结(一)
    数组练习题A财务管理
  • 原文地址:https://www.cnblogs.com/luyu1993/p/7544488.html
Copyright © 2011-2022 走看看