第二章 BIO与NIO
《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操作。
- Buffer:用于存取数据,最主要的是ByteBuffer
- 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的更多相关文章
- Netty学习--第二章 BIO的模型详解
一.什么是阻塞.非阻塞.同步.异步 我们以A线程调用B线程的过程例子来讲解这四个概念 在一个程序里,A调用B了,此时如果是 同步: A必须等待B返回结果后,才能继续执行,但是在这期间A会一直监控B的返 ...
- 第二章 NIO入门
传统的同步阻塞式I/O编程 基于NIO的非阻塞编程 基于NIO2.0的异步非阻塞(AIO)编程 为什么要使用NIO编程 为什么选择Netty 第二章 NIO 入门 2.1 传统的BIO编程 2.1.1 ...
- 操作系统层面聊聊BIO,NIO和AIO (epoll)
BIO 有了Block的定义,就可以讨论BIO和NIO了.BIO是Blocking IO的意思.在类似于网络中进行read, write, connect一类的系统调用时会被卡住. 举个例子,当用re ...
- Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!
本文原题“从实践角度重新理解BIO和NIO”,原文由Object分享,为了更好的内容表现力,收录时有改动. 1.引言 这段时间自己在看一些Java中BIO和NIO之类的东西,也看了很多博客,发现各种关 ...
- 【网络IO系列】IO的五种模型,BIO、NIO、AIO、IO多路复用、 信号驱动IO
前言 在上一篇文章中,我们了解了操作系统中内核程序和用户程序之间的区别和联系,还提到了内核空间和用户空间,当我们需要读取一条数据的时候,首先需要发请求告诉内核,我需要什么数据,等内核准备好数据之后 , ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 第二章Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...
- 从实践角度重新理解BIO和NIO
前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整,但是整个看下来之后,自己对NIO还是一知半解的状态,所以这 ...
- Java中BIO,NIO,AIO的理解
在高性能的I/O体系设计中,有几个概念常常会使我们感到迷惑不解.具体如下: 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7 ...
随机推荐
- 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 既然前后端 ...
- 使用Runnable接口创建线程
实现Runnable接口的类必须使用Thread类的实例才能创建线程.通过Runnable接口创建线程分为两步: 1.将实现Runnable接口的类实例化. 2.建立一个Thread对象,并将第一步实 ...
- android 滑动冲突
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 通过move事件的 拦截. 在滑动组件中,重写 在拦截触摸事件的时候 这个方法, 然后 ...
- hdu 4461 第37届ACM/ICPC杭州赛区I题
题意:给两个人一些棋子,每个棋子有其对应的power,若b没有或者c没有,或者二者都没有,那么他的total power就会减1,total power最少是1,求最后谁能赢 如果b或c出现的话,fl ...
- bzoj 2648: SJY摆棋子&&2716: [Violet 3]天使玩偶 --kdtree
2648: SJY摆棋子&&2716: [Violet 3]天使玩偶 Time Limit: 20 Sec Memory Limit: 128 MB Description 这天,S ...
- ZOJ 3765 Lights (伸展树splay)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3765 Lights Time Limit: 8 Seconds ...
- What is OpenOCD?
About OpenOCD was created by Dominic Rath as part of a 2005 diploma thesis written at the University ...
- 突破 BTrace 安全限制
http://blog.csdn.net/alivetime/article/details/6548615
- SystemParametersinfo的用法(一)
SystemParametersinfo的用法(一) 函数功能:该函数查询或设置系统级参数.该函数也可以在设置参数中更新用户配置文件. 函数原型:B00L SystemParametersinfo(U ...
- [Asp.net core]封装Layer ui checkbox 为taghelper
摘要 在使用checkbox,为其绑定值的时候,有些麻烦,就想着能不能用taghelper将其封装一下.直接上demo. 一个例子 using Microsoft.AspNetCore.Mvc.Ren ...