Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)
1.Java NIO 由以下几个核心部分组成:
- Channels(通道)
- Buffers(缓冲区)
- Selectors(选择器)
虽然Java NIO 中除此之外还有很多类和组件,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类。
(1)Channel 和 Buffer
基本上,所有的 IO操作在NIO 中都从一个Channel 开始。Channel 有点像 流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

Channel和Buffer有好几种类型。
下面是JAVA NIO中的一些主要Channel的实现:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
正如你所看到的,这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。
与这些类一起的有一些有趣的接口,但为简单起见,我尽量在概述中不提到它们。本教程其它章节与它们相关的地方我会进行解释。
以下是Java NIO里主要的Buffer实现:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。
Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件, 我也不打算在概述中说明。
(2)Selector
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
这是在一个单线程中使用一个Selector处理3个Channel的图示:

要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
2. NIO使用代码示例:
java NIO服务端和客户端代码实现,为了更好地理解java NIO,下面贴出服务端和客户端的简单代码实现:
服务端:
package com.himi.demo2; 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; /**
* NIO 服务端
*
* @author hebao
*/
public class NIOServer {
// 通道管理器
private Selector selector; /**
* 获得一个ServerSocket通道,并对该通道做一些初始化的工作
*
* @param port
* 绑定的端口号
* @throws IOException
*/
public void initServer(int port) throws IOException {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(new InetSocketAddress(port));
// 获得一个通道管理器
this.selector = Selector.open();
// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
// 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} /**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
*
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("服务端启动成功!");
// 轮询访问selector
while (true) {
// 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false); // 在这里可以给客户端发送信息哦
channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
// 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
} else if (key.isReadable()) {
read(key);
} } }
} /**
* 处理读取客户端发来的信息 的事件
*
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException {
// 服务器可读取消息:得到事件发生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("服务端收到信息:" + msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel.write(outBuffer);// 将消息回送给客户端
} /**
* 启动服务端测试
*
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(8000);
server.listen();
} }
客户端:
package com.himi.demo2; 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.SocketChannel;
import java.util.Iterator; /**
* NIO客户端
*
* @author hebao
*/
public class NIOClient {
// 通道管理器
private Selector selector; /**
* 获得一个Socket通道,并对该通道做一些初始化的工作
*
* @param ip
* 连接的服务器的ip
* @param port
* 连接的服务器的端口号
* @throws IOException
*/
public void initClient(String ip, int port) throws IOException {
// 获得一个Socket通道
SocketChannel channel = SocketChannel.open();
// 设置通道为非阻塞
channel.configureBlocking(false);
// 获得一个通道管理器
this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
// 用channel.finishConnect();才能完成连接
channel.connect(new InetSocketAddress(ip, port));
// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
channel.register(selector, SelectionKey.OP_CONNECT);
} /**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
*
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
// 轮询访问selector
while (true) {
selector.select();
// 获得selector中选中的项的迭代器
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 连接事件发生
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key.channel();
// 如果正在连接,则完成连接
if (channel.isConnectionPending()) {
channel.finishConnect(); }
// 设置成非阻塞
channel.configureBlocking(false); // 在这里可以给服务端发送信息哦
channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
// 在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
} else if (key.isReadable()) {
read(key);
} } }
} /**
* 处理读取服务端发来的信息 的事件
*
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException {
// 和服务端的read方法一样
} /**
* 启动客户端测试
*
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOClient client = new NIOClient();
client.initClient("localhost", 8000);
client.listen();
} }
Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)的更多相关文章
- Java基础知识强化之IO流笔记05:try...catch...finally包含的代码是运行期的
1. 代码示例: 上面看到的第13行: Date d = null;(这里必须初始化) 第14~20行使用try...catch...finally包含代码,这里的代码已经变成运行期代码.此时我们 ...
- Java基础知识强化之IO流笔记63:随机访问流RandomAccessFile
1. 随机访问流RandomAccessFile RandomAccessFile类不属于流,是Object类的子类.但它融合了InputStream和OutputStream的功能.支持对随机访问文 ...
- Java基础知识强化之IO流笔记36:InputStreamReader/OutputStreamWriter 复制文本文件案例
1. 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中. 数据源: a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader 目的地: b.t ...
- Java基础知识强化之IO流笔记17:FileOutputStream构造方法使用
1. 可以参照之前写的笔记: Android(java)学习笔记167:Java中操作文件的类介绍(File + IO流) 2. FileOutputStream(常用的)构造方法: FileOu ...
- Java基础知识强化之IO流笔记83:NIO与IO
当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景,以及它们如何影响您的代 ...
- Java基础知识强化之IO流笔记71:NIO之 NIO的(New IO流)介绍
1. I/O 简介 I/O ( 输入/输出 ):指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的. ...
- Java基础知识强化之IO流笔记68:Properties和IO流集合使用
1. Properties和IO流集合使用 这里的集合必须是Properties集合: public void load(Reader reader):把文件中的数据读取到集合中 public v ...
- Java基础知识强化之IO流笔记66:Properties的概述 和 使用(作为Map集合使用)
1. Properties的概述 Properties:属性集合类.是一个可以和IO流相结合使用的集合类. 该类主要用于读取以项目的配置文件(以.properties结尾的文件 和 xml文件). ...
- Java基础知识强化之IO流笔记64:合并流SequenceInputStream
1. SequenceInputStream合并流的概述: SequenceInputStream类可以将多个输入流串联在一起,合并为一个输入流,因此,该流也被称为合并流. 2. Sequence ...
随机推荐
- Windows PE3.0制作方法(从Win7中提取制作)
Windows PE3.0制作方法(从Win7中提取制作 在d:新建文件夹winpe,在winpe中新建sources.pe3和new文件夹,把附件中提供的工具imagex连文件夹一起放到winpe目 ...
- POJ-2785 4 Values whose Sum is 0(折半枚举 sort + 二分)
题目链接:http://poj.org/problem?id=2785 题意是给你4个数列.要从每个数列中各取一个数,使得四个数的sum为0,求出这样的组合的情况个数. 其中一个数列有多个相同的数字时 ...
- mysql中间件研究(Atlas,cobar,TDDL)
mysql-proxy是官方提供的mysql中间件产品可以实现负载平衡,读写分离,failover等,但其不支持大数据量的分库分表且性能较差.下面介绍几款能代替其的mysql开源中间件产品,Atlas ...
- cf754 B. Ilya and tic-tac-toe game
呵呵呵,这个题简直是一直在乱做,真是最近太弱了 #include<bits/stdc++.h> #define lowbit(x) x&(-x) #define LL long l ...
- OC:点语法
IOS中的@property 与 assign copy retain 的区别参考 //@理解为 OC 代码的标记 //如何去创建一个对象 创建对象的两步: // (1)为对象在堆区中开辟空间 Stu ...
- springmvc中forward和redirect
一.跳转 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; im ...
- CMSIS-DAP调试器
http://www.keil.com/support/man/docs/dapdebug/dapdebug_introduction.htm CMSIS-DAP is the interface f ...
- window.location.href的使用方法
http://hljqfl.blog.163.com/blog/static/40931580201122210573364/ 在写ASP.Net程序的时候,我们常常遇到跳转页面的问题,我们常常使用R ...
- 由“大数据量Excel入库高效方式”瞥见“并联系统”之优势
使用场景: 当你有一个Excel文件,需要把其中的数据高速录入到数据库中,文件中包含10万条以上数据. 设计方案: 我们将整个过程分成三个阶段,A(装载Excel文件). ...
- JavaScript代码优化(下载时间和执行速度优化)
JavaScript代码的速度被分成两部分:下载时间和执行速度. 下载时间 Web浏览器下载的是js源码,因此所有长变量名和注释都回包含在内.这个因素会增加下载时间.1160是一个TCP-IP包中的字 ...