《netty权威指南》读书笔记

一、BIO

1、服务端程序:

 package bio;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date; public class BioServer { public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8081);
Socket clientSocket = null;
while(true){
clientSocket = serverSocket.accept();//如果没有客户端接入,主线程阻塞在这里
new Thread(new ServerHandler(clientSocket)).start();
}
//finall关闭serverSocket
} } class ServerHandler implements Runnable{
private Socket clientSocket; public ServerHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
} @Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(this.clientSocket.getOutputStream(), true);
while(true){
String body = reader.readLine();
if (body==null){
break;
}
System.out.println(body);
writer.println(new Date().toString() + "->" + body);
}
} catch (IOException e) {
e.printStackTrace();
}
//finally关闭资源:流和socket
}
}
  • 服务端使用8081端口打开服务,不断接入客户端请求,每接入一个请求,都创建一个线程来处理这个请求。
  • 处理逻辑:读取客户端传来的信息,之后想客户端写信息。

2、客户端程序:

 package bio;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket; public class BioClient {
public static void main(String[] args) throws IOException {
Socket clientSocket = new Socket("127.0.0.1", 8081);
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
writer.println("haha");
String resp = reader.readLine();
System.out.println(resp);
//finall关闭serverSocket
}
}
  • 客户端创建socket去连接服务端,之后想服务端写信息,并且读取服务端传来的信息。

服务端阻塞的几个点:

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

二、NIO

1、服务端程序

 package nio;

 import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator; public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(8083));//监听链接8082端口的客户端socket
serverChannel.register(selector, SelectionKey.OP_ACCEPT);//将serverChannel注册到selector,并监听接受连接事件 while (selector.select() > 0) {//该方法会发生阻塞,直到至少有一个事件"准备就绪"为止
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey sk = it.next();
if (sk.isAcceptable()) {
SocketChannel clientChannel = serverChannel.accept();//相当于客户端三次握手
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
SocketChannel clientChannel = (SocketChannel) sk.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
while (clientChannel.read(buf) > 0) {//将通道中的数据读到缓冲区,因为clientChannel已经是非阻塞的,所以这里的read是非阻塞的
buf.flip();//将缓冲区由写模式切换到读模式
byte[] bytes = new byte[buf.remaining()];
buf.get(bytes);//将缓冲区中的数据读取到bytes中
String body = new String(bytes, "UTF-8");
System.out.println("接收到来自客户端的信息:" + body);
buf.clear();
}
}
it.remove();
}
}
}
}
  • nio三组件:

    • Buffer:用于存取数据,最主要的是ByteBuffer

      • position:下一个将被操作的字节位置
      • limit:在写模式下表示可以进行写的字节数,在读模式下表示可以进行读的字节数
      • capacity:Buffer的大小
    • Channel:用于传输数据,与Buffer相互配合
    • Selector:多路复用器。轮询注册在其上的Channel,当发现某个或者多个Channel处于“就绪状态”后(有新的TCP链接接入、读、写事件),从阻塞状态返回就绪的Channel的SelectionKey集合,之后进行IO操作。
  • selector.select():阻塞,直到有“就绪事件”发生或抛出异常

2、客户端程序

 package nio;

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

附: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操作(编解码、业务逻辑等)

第二章 BIO与NIO的更多相关文章

  1. Netty学习--第二章 BIO的模型详解

    一.什么是阻塞.非阻塞.同步.异步 我们以A线程调用B线程的过程例子来讲解这四个概念 在一个程序里,A调用B了,此时如果是 同步: A必须等待B返回结果后,才能继续执行,但是在这期间A会一直监控B的返 ...

  2. 第二章 NIO入门

    传统的同步阻塞式I/O编程 基于NIO的非阻塞编程 基于NIO2.0的异步非阻塞(AIO)编程 为什么要使用NIO编程 为什么选择Netty 第二章 NIO 入门 2.1 传统的BIO编程 2.1.1 ...

  3. 操作系统层面聊聊BIO,NIO和AIO (epoll)

    BIO 有了Block的定义,就可以讨论BIO和NIO了.BIO是Blocking IO的意思.在类似于网络中进行read, write, connect一类的系统调用时会被卡住. 举个例子,当用re ...

  4. Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!

    本文原题“从实践角度重新理解BIO和NIO”,原文由Object分享,为了更好的内容表现力,收录时有改动. 1.引言 这段时间自己在看一些Java中BIO和NIO之类的东西,也看了很多博客,发现各种关 ...

  5. 【网络IO系列】IO的五种模型,BIO、NIO、AIO、IO多路复用、 信号驱动IO

    前言 在上一篇文章中,我们了解了操作系统中内核程序和用户程序之间的区别和联系,还提到了内核空间和用户空间,当我们需要读取一条数据的时候,首先需要发请求告诉内核,我需要什么数据,等内核准备好数据之后 , ...

  6. 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域  

  7. 第二章Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...

  8. 从实践角度重新理解BIO和NIO

    前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整,但是整个看下来之后,自己对NIO还是一知半解的状态,所以这 ...

  9. Java中BIO,NIO,AIO的理解

    在高性能的I/O体系设计中,有几个概念常常会使我们感到迷惑不解.具体如下: 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7  ...

随机推荐

  1. SDC_ETL融合数据产品白皮书

    SDC_ETL融合数据产品白皮书 http://www.sefonsoft.com/?s=/home/pro/pdf/id/48.html

  2. html5解决ajax破坏浏览器机制

    pjax是一种基于ajax+history.pushState的新技术,该技术可以无刷新改变页面的内容,并且可以改变页面的URL.pjax是ajax+pushState的封装,同时支持本地存储.动画等 ...

  3. hdu 1116 敌兵布阵(树状数组区间求和)

    题意: 给出一行数字,然后可以修改其中第i个数字,并且可以询问第i至第j个数字的和(i <= j). 输入: 首行输入一个t,表示共有t组数据. 接下来每行首行输入一个整数n,表示共有n个数字. ...

  4. [TJOI2015]线性代数

    OJ题号:BZOJ3996 题目大意: 给定一个矩阵$B_{nn}$,矩阵$C_{1n}$,存在一个01矩阵$A_{1,n}$使得$d=(A\times B-c)\times A^\mathsf{T} ...

  5. hdu 2710 水题

    题意:判断一些数里有最大因子的数 水题,省赛即将临近,高效的代码风格需要养成,为了简化代码,以后可能会更多的使用宏定义,但是通常也只是快速拿下第一道水题,涨自信.大部分的代码还是普通的形式,实际上能简 ...

  6. 【BZOJ-2063】我爸是李刚 数位dp 好题

    2063: 我爸是李刚 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 139  Solved: 72[Submit][Status][Discuss] ...

  7. Codeforces 986D Perfect Encoding FFT

    题意: 给定一个数n,选出m个数使得 $\Pi_{i=1}^m a_i\ge n$,求$\sum_{i=1}^m a_i$的最小值 ,其中$m$的大小不限 $n$的长度$\le 10^6$ 简单的计算 ...

  8. c# -- 解决vs使用本地iis运行项目支持局域网访问的问题(附防火墙端口开放步骤)

    用vs运行项目时,有时候需要局域网内不同设备进行访问调试~ 以前解决过这个问题,这次用了部新电脑,问题又出现了,改了配置还是不行,原来还差了一步防火墙端口开放访问. 于是写了这篇分享,备忘~ 操作步骤 ...

  9. [BZOJ1115][POI2009]石子游戏Kam解题报告|阶梯博弈

    有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数.两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏.问先手是否必胜. 首先 ...

  10. java 实现生产者-消费者模式

    生产和消费者模式有很多种,现在介绍几种常见的方式 wait/notify实现生产和消费者模式 1.使用wait/notify实现生产和消费者模式: public class Depot { // 实际 ...