第二章 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 }

  • 服务端使用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 }

  • 客户端创建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 }

  • 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 }

附: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操作(编解码、业务逻辑等)
时间: 2024-10-10 07:18:36

第二章 BIO与NIO的相关文章

NIO入门系列之第二章:通道和缓冲区

第2章 通道和缓冲区 2.1  概述 通道和缓冲区是 NIO 中的核心对象,几乎在每一个I/O 操作中都要使用它们. 通道是对原 I/O 包中的流的模拟.到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象.一个 Buffer 实质上是一个容器对象.发送给一个通道的所有对象都必须首先放到缓冲区中:同样地,从通道中读取的任何数据都要读到缓冲区中. 2.2  什么是缓冲区? Buffer 是一个对象,它包含一些要写入或者刚读出的数据.在 NIO 中加入 Buffer 对象,体

Java BIO、NIO、AIO 学习

先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API). 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回). 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你

《Netty In Action中文版》第二章:第一个Netty程序

注:本篇内容出自<Netty In Action>一书:         注:本人原创译文,转载请注明出处! 本章介绍 获取Netty4最新版本 设置运行环境来构建和运行netty程序 创建一个基于Netty的服务器和客户端 拦截和处理异常 编写和运行Netty服务器和客户端 本章将简单介绍Netty的核心概念,这个狠心概念就是学习Netty是如何拦截和处理异常,对于刚开始学习netty的读者,利用netty的异常拦截机制来调试程序问题很有帮助.本章还会介绍其他一些核心概念,如服务器和客户端的

Java的中BIO、NIO、AIO-1

Java的中BIO.NIO.AIO-1 java 最近在项目中用到TCP通信来完成命令和运行结果的交互,用的是典型的TCP通信中的C/S架构,原因很简单:在业务需求低的环境下,这种架构简单.稳定还容易写.但是在实际部署的情况下,一直出现读不到数据的空指针异常,按说BIO模式开发的应该阻塞直到有数据读取,没有找到原因就变通写了一个消息队列,使用定时器每1s从定时器中拿数据,解决了这个问题.但是想想这种同步阻塞的形式,就想了解一下其他的模式:NIO.AIO.好了,啰嗦了好多,进入正题: IO操作的基

NIO 源码分析(03) 从 BIO 到 NIO

目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 二.BIO 和 NIO 的区别 2.1 BIO 面向流,NIO 面向缓冲区. Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel

基础 | BIO、NIO与AIO

本文链接:https://blog.csdn.net/bingbeichen/article/details/83617163 Java中的IO部分比较复杂,具体可参看书籍<Java NIO>和<Netty权威指南>.在此,仅对BIO.NIO和AIO进行概述性梳理,未涉及到具体实现细节,后续有空将深入展开. 同步IO和异步IO参考答案: IO操作主要分为两个步骤,即发起IO请求和实际IO操作,同步IO与异步IO的区别就在于第二个步骤是否阻塞. 若实际IO操作阻塞请求进程,即请求进程

Java世界里的BIO,NIO,AIO

作为一名程序员,io知识是必不可少,其实一直在和io打交道,要么显示要么隐含给了操作系统,故做下关于io的记录.说io之前呢,先介绍什么叫同步异步丶阻塞非阻塞 1.  同步异步丶阻塞非阻塞 1.1 同步是指发出一个请求,在没有得到结果之前该请求就不返回结果,请求返回时,也就得到结果了.比如我经常用烧水壶烧水,没烧开前一直盯着水壶(等水开). 1.2 异步是指发出一个请求后,立刻得到了回应,但没有返回结果,这时我们可以再处理别的事情(发送其他请求),所以这种方式需要我们通过状态主动查看是否有了结果

Machine Learning In Action 第二章学习笔记: kNN算法

本文主要记录<Machine Learning In Action>中第二章的内容.书中以两个具体实例来介绍kNN(k nearest neighbors),分别是: 约会对象预测 手写数字识别 通过“约会对象”功能,基本能够了解到kNN算法的工作原理.“手写数字识别”与“约会对象预测”使用完全一样的算法代码,仅仅是数据集有变化. 约会对象预测 1 约会对象预测功能需求 主人公“张三”喜欢结交新朋友.“系统A”上面注册了很多类似于“张三”的用户,大家都想结交心朋友.“张三”最开始通过自己筛选的

Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)

转载请注明出处:http://blog.csdn.net/anxpp/article/details/51512200,谢谢! 本文会从传统的BIO到NIO再到AIO自浅至深介绍,并附上完整的代码讲解. 下面代码中会使用这样一个例子:客户端发送一段算式的字符串到服务器,服务器计算后返回结果到客户端. 代码的所有说明,都直接作为注释,嵌入到代码中,看代码时就能更容易理解,代码中会用到一个计算结果的工具类,见文章代码部分. 相关的基础知识文章推荐: Linux 网络 I/O 模型简介(图文) Jav