第二章 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 ...
随机推荐
- 当Java遇到XML 的邂逅+dom4j
XML简介: XML:可扩展标记语言! 01.很象html 02.着重点是数据的保存 03.无需预编译 04.符合W3C标准 可扩展:我们可以自定义,完全按照自己的规则来! 标记: 计算机所能认识的信 ...
- BZOJ.4946.[NOI2017]蔬菜(贪心 离线)
题目链接 因为有删除,考虑倒序处理某个p的询问. 那么每天删除xi的蔬菜就变成了每天运来xi的蔬菜.那么我们取当前最优的即可,早取晚取都一样,不需要留给后面取,还能给后面更优的留出空间. 这样就只需考 ...
- C++最快的读取文件的方案(scanf,cin(及取消sync),fread)的详细对比
竞赛中,遇到大数据时,往往读文件成了程序运行速度的瓶颈,需要更快的读取方式.相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头,于是从此以后发誓不用cin读数据.还有人说Pascal的rea ...
- BZOJ4277 : [ONTAK2015]Cięcie
假设分成如下三段: [1..i][i+1..j][j+1..n] 考虑中间那一段,设f[i]为前i位组成的数模q的值,pow[i]为$10^i$模q的值,那么有: f[j]-f[i]*pow[j-i] ...
- Codeforces Round #394 (Div. 2) C. Dasha and Password 暴力
C. Dasha and Password 题目连接: http://codeforces.com/contest/761/problem/C Description After overcoming ...
- ip定位
http://www.cnblogs.com/pengcc/p/5294836.html https://wx.jdcloud.com/shop/shopDetail/RTBAsia,里面有各种IP地 ...
- [原创]用Charles模拟App各种网络带宽测试介绍
[原创]用Charles模拟App各种网络带宽测试介绍 相信每个测试在进行自己公司App测试时,都会碰到一个问题,如何去模拟各种App在各种带宽下的测试情况,估计很少有公司直接去采用2g/3g/4g卡 ...
- CoreSight™ Technology
ARM Cortex-M processor-based devices use the ARM CoreSight technology which introduces powerful new ...
- iOS十进制切割格式转换
//@"123456789" 转换后 @"123,456,789" @interface NSString (num) - (NSString *)money; ...
- Html学习笔记3
1表格的标题和表头: <table> <caption>成绩单</caption> <tr> <th>姓名</th> <t ...