zoukankan      html  css  js  c++  java
  • 第二章 BIO与NIO

    《netty权威指南》读书笔记

    一、BIO

    1、服务端程序:

     1 package bio;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStreamReader;
     6 import java.io.PrintWriter;
     7 import java.net.ServerSocket;
     8 import java.net.Socket;
     9 import java.util.Date;
    10 
    11 public class BioServer {
    12 
    13     public static void main(String[] args) throws IOException {
    14         ServerSocket serverSocket = new ServerSocket(8081);
    15         Socket clientSocket = null;
    16         while(true){
    17             clientSocket = serverSocket.accept();//如果没有客户端接入,主线程阻塞在这里
    18             new Thread(new ServerHandler(clientSocket)).start();
    19         }
    20         //finall关闭serverSocket
    21     }
    22 
    23 }
    24 
    25 class ServerHandler implements Runnable{
    26     private Socket clientSocket;
    27 
    28     public ServerHandler(Socket clientSocket) {
    29         this.clientSocket = clientSocket;
    30     }
    31 
    32     @Override
    33     public void run() {
    34         try {
    35             BufferedReader reader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
    36             PrintWriter writer = new PrintWriter(this.clientSocket.getOutputStream(), true);
    37             while(true){
    38                 String body = reader.readLine();
    39                 if (body==null){
    40                     break;
    41                 }
    42                 System.out.println(body);
    43                 writer.println(new Date().toString() + "->" + body);
    44             }
    45         } catch (IOException e) {
    46             e.printStackTrace();
    47         }
    48         //finally关闭资源:流和socket
    49     }
    50 }
    View Code
    • 服务端使用8081端口打开服务,不断接入客户端请求,每接入一个请求,都创建一个线程来处理这个请求。
    • 处理逻辑:读取客户端传来的信息,之后想客户端写信息。

    2、客户端程序:

     1 package bio;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStreamReader;
     6 import java.io.PrintWriter;
     7 import java.net.Socket;
     8 
     9 public class BioClient {
    10     public static void main(String[] args) throws IOException {
    11         Socket clientSocket = new Socket("127.0.0.1", 8081);
    12         BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
    13         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
    14         writer.println("haha");
    15         String resp = reader.readLine();
    16         System.out.println(resp);
    17         //finall关闭serverSocket
    18     }
    19 }
    View Code
    • 客户端创建socket去连接服务端,之后想服务端写信息,并且读取服务端传来的信息。

    服务端阻塞的几个点:

    • serverSocket.accept();//如果没有客户端接入,主线程阻塞在这里
    • 输入流InputStream的read操作也会阻塞:直到“有数据可读”或者“可用数据读取完毕”或者“发生异常”
    • 输出流OutputStream的write操作也会阻塞:直到“所有要发送的字节全部写入”或者“发生异常”

    二、NIO

    1、服务端程序

     1 package nio;
     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 
    12 public class NioServer {
    13     public static void main(String[] args) throws IOException {
    14         Selector selector = Selector.open();
    15 
    16         ServerSocketChannel serverChannel = ServerSocketChannel.open();
    17         serverChannel.configureBlocking(false);
    18         serverChannel.socket().bind(new InetSocketAddress(8083));//监听链接8082端口的客户端socket
    19         serverChannel.register(selector, SelectionKey.OP_ACCEPT);//将serverChannel注册到selector,并监听接受连接事件
    20 
    21         while (selector.select() > 0) {//该方法会发生阻塞,直到至少有一个事件"准备就绪"为止
    22             Iterator<SelectionKey> it = selector.selectedKeys().iterator();
    23             while (it.hasNext()) {
    24                 SelectionKey sk = it.next();
    25                 if (sk.isAcceptable()) {
    26                     SocketChannel clientChannel = serverChannel.accept();//相当于客户端三次握手
    27                     clientChannel.configureBlocking(false);
    28                     clientChannel.register(selector, SelectionKey.OP_READ);
    29                 } else if (sk.isReadable()) {
    30                     SocketChannel clientChannel = (SocketChannel) sk.channel();
    31                     ByteBuffer buf = ByteBuffer.allocate(1024);
    32                     while (clientChannel.read(buf) > 0) {//将通道中的数据读到缓冲区,因为clientChannel已经是非阻塞的,所以这里的read是非阻塞的
    33                         buf.flip();//将缓冲区由写模式切换到读模式
    34                         byte[] bytes = new byte[buf.remaining()];
    35                         buf.get(bytes);//将缓冲区中的数据读取到bytes中
    36                         String body = new String(bytes, "UTF-8");
    37                         System.out.println("接收到来自客户端的信息:" + body);
    38                         buf.clear();
    39                     }
    40                 }
    41                 it.remove();
    42             }
    43         }
    44     }
    45 }
    View Code
    • nio三组件:
      • Buffer:用于存取数据,最主要的是ByteBuffer
        • position:下一个将被操作的字节位置
        • limit:在写模式下表示可以进行写的字节数,在读模式下表示可以进行读的字节数
        • capacity:Buffer的大小
      • Channel:用于传输数据,与Buffer相互配合
      • Selector:多路复用器。轮询注册在其上的Channel,当发现某个或者多个Channel处于“就绪状态”后(有新的TCP链接接入、读、写事件),从阻塞状态返回就绪的Channel的SelectionKey集合,之后进行IO操作。
    • selector.select():阻塞,直到有“就绪事件”发生或抛出异常

    2、客户端程序

     1 package nio;
     2 
     3 import java.io.IOException;
     4 import java.net.InetSocketAddress;
     5 import java.nio.ByteBuffer;
     6 import java.nio.channels.SocketChannel;
     7 
     8 public class NioClient {
     9     public static void main(String[] args) throws IOException {
    10         SocketChannel clientChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8083));//向服务端发出连接请求,服务端会通过accept()方法实现三次握手后,建立连接
    11         clientChannel.configureBlocking(false);
    12         ByteBuffer buf = ByteBuffer.allocate(1024);//分配在JVM堆中:capacity=1024;position=0;limit=1024
    13 //        ByteBuffer buf = ByteBuffer.allocateDirect(1024);//分配在堆外内存(物理内存)中
    14         buf.put("abcde".getBytes());//将数据写入缓冲区buf中:capacity=1024;position=5;limit=1024
    15         buf.flip();//将缓冲区从写模式切换到读模式:capacity=1024;position=0;limit=5
    16         clientChannel.write(buf);//将缓冲区的数据写入通道:capacity=1024;position=5;limit=5
    17         buf.clear();//清空缓冲区:capacity=1024;position=0;limit=1024
    18         clientChannel.close();
    19     }
    20 }
    View Code

    附:ByteBuffer使用JVM堆内存和使用堆外内存的区别:(图片来自尚硅谷的NIO教程)

    • 使用堆内存:
      • 应用程序将数据写入用户地址空间(JVM)中,之后将用户地址空间中的数据拷贝到内核地址空间(操作系统)
    • 使用堆外内存:
      • 直接在物理内存开辟空间,应用程序直接将数据写入物理内存,之后操作系统将其写入硬盘 - “零拷贝”

    三、BIO与NIO的比较

    1、线程数

    • BIO:一个客户端连接就要使用一个服务端线程来进行处理
      • 可能会有大量的想爱你成处于休眠状态,只是等待输入或输出(阻塞)
      • 为每个线程分配调用栈,大约1M,服务端内存吃紧
      • 即使服务端内存很大,线程数太大会导致线程上下文切换浪费大量时间
    • NIO:一个服务端线程操作一个Selector,就可以处理成千上万的客户端连接

    2、阻塞情况

    • BIO:读、写、接受连接都会发生阻塞
    • NIO:只有Selector.select()会阻塞,其实是等待Channel上的“就绪事件”

    3、面向对象

    • BIO:面向流
    • NIO:面向Buffer

    4、适合点

    • BIO:如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合
    • NIO:如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。

    四、Reactor模型

    主从模型:

    • 主线程池:
      • 全部由NIO线程组成,使用线程池是因为担心性能问题
      • 接收客户端连接请求,可能包含认证
      • 接收到客户端的连接请求并处理完成(比如认证)后,将创建出来的SocketChannel(查看上边的NIOServer类)注册到次线程池的某一条NIO线程上,之后这条NIO线程进行IO操作。
    • 次线程池:
      • 全部由NIO线程组成
      • 进行IO操作(编解码、业务逻辑等)
  • 相关阅读:
    Azure PowerShell (2) 修改Azure订阅名称
    Windows Azure Platform Introduction (11) 了解Org ID、Windows Azure订阅、账户
    Azure PowerShell (3) 上传证书
    Azure PowerShell (1) PowerShell入门
    Windows Azure Service Bus (2) 队列(Queue)入门
    Windows Azure Service Bus (1) 基础
    Windows Azure Cloud Service (10) Role的生命周期
    Windows Azure Cloud Service (36) 在Azure Cloud Service配置SSL证书
    Android studio 使用心得(一)—android studio快速掌握快捷键
    android 签名、混淆打包
  • 原文地址:https://www.cnblogs.com/java-zhao/p/7044809.html
Copyright © 2011-2022 走看看