JAVA NIO基础知识总结二_2012-1-9
- 格式:doc
- 大小:239.00 KB
- 文档页数:9
java nio原理Java NIO(New IO)是Java SE 1.4版本中添加的一个用于高效处理 I/O 操作的API。
与传统的Java IO API相比,Java NIO提供了更好的可扩展性和可靠性。
在本文中,我们将深入探讨Java NIO的原理。
1. Java NIO的基本组件Java NIO的核心组件由以下四个部分构成:- 缓冲区(Buffer):一个可读写的数据容器,用于存储原始数据。
- 通道(Channel):用于在缓冲区和外部实体(如文件、套接字)之间传输数据。
- 选择器(Selector):非阻塞I/O的关键。
选择器可以同时监听多个通道的I/O事件,从而实现单线程处理多通道数据的能力。
- 选择键(SelectionKey):表示一个通道在选择器中的注册状态及其对应的I/O事件。
2. Java NIO的工作流程- 创建一个Channel实例:选择要使用的通道类型(如FileChannel)。
- 创建一个Buffer实例:指定缓冲区的大小。
- 将数据写入缓冲区:可通过put()方法向缓冲区写入数据。
- 调用flip()方法:将Buffer从写入模式切换到读取模式。
- 将数据从Buffer读取到Channel中:可使用Channel.write()方法将数据从Buffer中写出到通道中。
- 关闭Channel和Buffer实例:在处理完一个数据块后需要关闭Channel和Buffer实例,以释放资源。
3. Java NIO的优势相比传统的Java IO API,Java NIO的主要优势有:- 更快的速度:由于Java NIO使用缓冲区和通道等组件,因此在处理大量数据时,它比Java IO API更快。
- 更少的资源占用:Java NIO使用少量的线程和较少的内存资源,因此它比Java IO API更适合于处理高并发的应用程序。
- 可扩展性强:Java NIO API的原始设计使它易于扩展,因此它适合于开发更复杂的网络应用程序。
Java Api nio包学习李君整理1.引言I/O流或者输入/输出流指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。
新的输入/输出(NIO)库是在JDK 1.4中引入的。
NIO弥补了原来的I/O的不足,它在标准Java代码中提供了高速的、面向块的I/O。
原来的I/O库与NIO最重要的区别是数据打包和传输的方式的不同,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O系统一次一个字节地处理数据。
一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。
为流式数据创建过滤器非常容易。
链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。
不利的一面是,面向流的I/O通常相当慢。
NIO与原来的I/O有同样的作用和目的,但是它使用块I/O的处理方式。
每一个操作都在一步中产生或者消费一个数据块。
按块处理数据比按(流式的)字节处理数据要快得多。
但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。
2.从一个例子开始下面我们从一个简单的使用IO和NIO读取一个文件中的内容为例,来进入NIO的学习之旅。
使用IO来读取指定文件中的前1024字节并打印出来:Java代码1/**2*使用IO读取指定文件的前1024个字节的内容。
3*@param file指定文件名称。
4*@throws java.io.IOException IO异常。
5*/6public void ioRead(String file)throws IOException{7FileInputStream in=new FileInputStream(file);8byte[]b=new byte[1024];9in.read(b);10System.out.println(new String(b));11}1213/**14*使用NIO读取指定文件的前1024个字节的内容。
JavaNIO详解(⼀)⼀、基本概念描述1.1 I/O简介I/O即输⼊输出,是计算机与外界世界的⼀个借⼝。
IO操作的实际主题是操作系统。
在java编程中,⼀般使⽤流的⽅式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象⼀次移动⼀个字节。
流IO负责把对象转换为字节,然后再转换为对象。
关于Java IO相关知识请参考我的另⼀篇⽂章:1.2 什么是NIONIO即New IO,这个库是在JDK1.4中才引⼊的。
NIO和IO有相同的作⽤和⽬的,但实现⽅式不同,NIO主要⽤到的是块,所以NIO的效率要⽐IO⾼很多。
在Java API中提供了两套NIO,⼀套是针对标准输⼊输出NIO,另⼀套就是⽹络编程NIO,本篇⽂章重点介绍标NIO,关于⽹络编程NIO请见。
1.3 流与块的⽐较NIO和IO最⼤的区别是数据打包和传输⽅式。
IO是以流的⽅式处理数据,⽽NIO是以块的⽅式处理数据。
⾯向流的IO⼀次⼀个字节的处理数据,⼀个输⼊流产⽣⼀个字节,⼀个输出流就消费⼀个字节。
为流式数据创建过滤器就变得⾮常容易,链接⼏个过滤器,以便对数据进⾏处理⾮常⽅便⽽简单,但是⾯向流的IO通常处理的很慢。
⾯向块的IO系统以块的形式处理数据。
每⼀个操作都在⼀步中产⽣或消费⼀个数据块。
按块要⽐按流快的多,但⾯向块的IO缺少了⾯向流IO 所具有的有雅兴和简单性。
⼆、NIO基础Buffer和Channel是标准NIO中的核⼼对象(⽹络NIO中还有个Selector核⼼对象,具体请参考),⼏乎每⼀个IO操作中都会⽤到它们。
Channel是对原IO中流的模拟,任何来源和⽬的数据都必须通过⼀个Channel对象。
⼀个Buffer实质上是⼀个容器对象,发给Channel的所有对象都必须先放到Buffer中;同样的,从Channel中读取的任何数据都要读到Buffer中。
2.1 关于BufferBuffer是⼀个对象,它包含⼀些要写⼊或读出的数据。
Java NIO API详解NIO API主要集中在java.nio和它的subpackages中:java.nio定义了Buffer及其数据类型相关的子类。
其中被java.nio.channels中的类用来进行IO操作的ByteBuffer的作用非常重要。
java.nio.channels定义了一系列处理IO的Channel接口以及这些接口在文件系统和网络通讯上的实现。
通过Selector这个类,还提供了进行非阻塞IO操作的办法。
这个包可以说是NIO API的核心。
java.nio.channels.spi定义了可用来实现channel和selector API的抽象类。
java.nio.charset定义了处理字符编码和解码的类。
java.nio.charset.spi定义了可用来实现charset API的抽象类。
java.nio.channels.spi和java.nio.charset.spi这两个包主要被用来对现有NIO API进行扩展,在实际的使用中,我们一般只和另外的3个包打交道。
下面将对这3个包一一介绍。
Package java.nio这个包主要定义了Buffer及其子类。
Buffer定义了一个线性存放primitive type数据的容器接口。
对于除boolean以外的其他primitive type,都有一个相应的Buffer子类,ByteBuffer是其中最重要的一个子类。
下面这张UML类图描述了java.nio中的类的关系:Buffer定义了一个可以线性存放primitive type数据的容器接口。
Buffer主要包含了与类型(byte, char…)无关的功能。
值得注意的是Buffer及其子类都不是线程安全的。
每个Buffer都有以下的属性:capacity这个Buffer最多能放多少数据。
capacity一般在buffer被创建的时候指定。
limit在Buffer上进行的读写操作都不能越过这个下标。
nio源码解析一、引言1.背景介绍在Java 编程中,IO 操作一直是开发者关注的重点。
随着Java 技术的不断发展,Java NIO(New I/O)作为一种全新的IO 处理方式,逐渐成为Java 开发者必备技能。
本文将对Java NIO 进行详细的解析,帮助读者更好地理解和应用这一技术。
2.NIO 概念阐述IO,全称Non-Blocking I/O,中文意为非阻塞IO。
与传统的阻塞IO 不同,NIO 采用了事件驱动模型,使得IO 操作与线程操作分离,从而在处理高并发、高性能场景下具有明显优势。
二、Java NIO 核心API1.ChannelChannel 是Java NIO 的核心接口,代表了一个IO 通道。
它可以用于读写数据,并且支持多种类型的IO 操作。
在Java NIO 中,Channel 分为字节通道(ByteChannel)和字符通道(CharacterChannel)等。
2.BufferBuffer 是Java NIO 的另一个核心接口,用于存储数据。
Buffer 内部采用环形缓冲区实现,可以有效提高数据处理效率。
Buffer 提供了多种方法,方便开发者进行数据的添加、删除和检查等操作。
3.SelectorSelector 是Java NIO 中用于处理多路IO 事件的工具。
通过注册通道(RegisteredChannel )将通道与Selector 关联,然后使用Selector 的select 方法监听IO 事件。
当IO 事件发生时,Selector 会返回一个包含事件通道的集合,开发者可以根据需要进行相应处理。
三、Java NIO 的工作原理1.事件驱动模型Java NIO 采用了事件驱动模型,将IO 操作与线程操作分离。
当IO 操作就绪时,操作系统会通过epoll 等机制通知Java 程序,此时线程可以处理该事件。
这种方式避免了线程的阻塞,提高了程序的并发处理能力。
2.非阻塞IOJava NIO 中的Channel 接口默认采用非阻塞IO。
JavaNIO原理图⽂分析及代码实现Java IO在Client/Server模型中,Server往往需要同时处理⼤量来⾃Client的访问请求,因此Server端需采⽤⽀持⾼并发访问的架构。
⼀种简单⽽⼜直接的解决⽅案是“one-thread-per-connection”。
这是⼀种基于阻塞式I/O的多线程模型。
在该模型中,Server为每个Client连接创建⼀个处理线程,每个处理线程阻塞式等待可能达到的数据,⼀旦数据到达,则⽴即处理请求、返回处理结果并再次进⼊等待状态。
由于每个Client连接有⼀个单独的处理线程为其服务,因此可保证良好的响应时间。
但当系统负载增⼤(并发请求增多)时,Server端需要的线程数会增加,这将成为系统扩展的瓶颈所在。
()<IMG style="DISPLAY: block; MARGIN-LEFT: auto; MARGIN-RIGHT: auto" title=旧I/O模型,多个线程阻塞等待客户端请求 alt=旧I/O模型,多个线程阻塞等待客户端请求 src="http://www.flyoung.me/assets/images/javaio.jpg">Java NIOJava NIO不但引⼊了全新的⾼效的I/O机制,同时引⼊了基于Reactor设计模式的多路复⽤异步模式。
NIO包中主要包含以下⼏种抽象数据类型。
* Channel(通道):NIO把它⽀持的I/O对象抽象为Channel。
它模拟了通信连接,类似于原I/O中的流(Stream),⽤户可以通过它读取和写⼊数据。
⽬前已知的实例类有SocketChannel、ServerSocketChannel、DatagramChannel、 FileChannel等。
* Buffer(缓冲区):Buffer是⼀块连续的内存区域,⼀般作为Channel收发数据的载体出现。
java根底篇新I O技术(NIO) 电脑资料在JDK1.4以前,I/O输入输出处理,我们把它称为旧I/O处理,在JDK1.4开始,java提供了一系列改良的输入/输出新特性,这些功能被称为新I/O(NEW I/O),新添了许多用于处理输入/输出的类,这些类都被放在java.nio包及子包下,并且对原java.io包中的很多类以NIO为根底进行了改写,新添了满足新I/O的功能,Java NIO和IO的主要区别IONIO面向流面向缓冲阻塞IO非阻塞IO无选择器面向缓冲(Buffer)在整个Java的心I/O中,所以操作都是以缓冲区进行的,使操作的性能大大提高。
操作在Buffer中存在一系列的状态变量,这状态变量随着写入或读取都可能会被概念,在缓冲区开元使用是三个值表示缓冲区的状态。
position:表示下个缓冲区读取或写入的操作指针,没向缓冲区中华写入数据的时候此指针就会改变,指针永远放在写入的最后一个元素之后。
即:如果写入了4个位置的数据,那么posotion会指向第5个位置。
Limit:表示还有多少数据可以存储或读取,position<=limit capacity:表示缓冲区的最大容量,limit<=capacity,此值在分配缓冲区时被设置。
一般不改变。
创立缓冲区:import java.nio.IntBuffer ;public class IntBufferDemo{public static void main(String args[]){IntBuffer buf = IntBuffer.allocate(10) ; // 准备出10个大小的缓冲区System.out.print("1、写入数据之前的position、limit和capacity:") ;System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;int temp[] = {5,7,9} ;// 定义一个int数组buf.put(3) ; // 设置一个数据buf.put(temp) ; // 此时已经存放了四个记录System.out.print("2、写入数据之后的position、limit和capacity:") ;System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;buf.flip() ; // 重设缓冲区// postion = 0 ,limit = 原本positionSystem.out.print("3、准备输出数据时的position、limit和capacity:") ;System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ; System.out.print("缓冲区中的内容:") ;while(buf.hasRemaining()){int x = buf.get() ;System.out.print(x + "、") ;}}}如果创立了缓冲区,那么JVM可直接对其执行本机的IO操作import java.nio.ByteBuffer ;public class ByteBufferDemo{public static void main(String args[]){ByteBuffer buf = ByteBuffer.allocateDirect(10) ; // 准备出10个大小的缓冲区byte temp[] = {1,3,5,7,9} ; // 设置内容buf.put(temp) ; // 设置一组内容buf.flip() ;System.out.print("主缓冲区中的内容:") ;while(buf.hasRemaining()){int x = buf.get() ;System.out.print(x + "、") ;}}}通道(Channel)Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。
JavaNIO理解与使⽤Netty的使⽤或许我们看着官⽹user guide还是很容易⼊门的。
因为java nio使⽤⾮常的繁琐,netty对java nio进⾏了⼤量的封装。
对于Netty的理解,我们⾸先需要了解NIO的原理和使⽤。
所以,我也特别渴望去了解NIO这种通信模式。
官⽅的定义是:nio 是non-blocking的简称,在jdk1.4 ⾥提供的新api 。
Sun 官⽅标榜的特性如下:为所有的原始类型提供(Buffer)缓存⽀持。
字符集编码解码解决⽅案。
Channel :⼀个新的原始I/O 抽象。
⽀持锁和内存映射⽂件的⽂件访问接⼝。
提供多路(non-bloking) ⾮阻塞式的⾼伸缩性⽹络I/O 。
是不是很抽象?在阅读这篇技术⽂档之后,收获了很多。
包括对Java NIO的理解和使⽤,所以也特别的感谢作者。
⾸先,还是来回顾以下从这篇⽂档中学到的要点。
为什么要使⽤ NIO?NIO 的创建⽬的是为了让 Java 程序员可以实现⾼速 I/O ⽽⽆需编写⾃定义的本机代码。
NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因⽽可以极⼤地提⾼速度。
NIO最重要的组成部分通道 Channels缓冲区 Buffers选择器 SelectorsBuffer 是⼀个对象,它包含⼀些要写⼊或者刚读出的数据。
在 NIO 库中,所有数据都是⽤缓冲区处理的。
在读取数据时,它是直接读到缓冲区中的。
在写⼊数据时,它是写⼊到缓冲区中的。
任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。
缓冲区实质上是⼀个数组。
通常它是⼀个字节数组,但是也可以使⽤其他种类的数组。
但是⼀个缓冲区不仅仅是⼀个数组。
缓冲区提供了对数据的结构化访问,⽽且还可以跟踪系统的读/写进程。
Channel是⼀个对象,可以通过它读取和写⼊数据看完下⾯这个例⼦,基本上就理解buffer和channel的作⽤了1. package yyf.java.nio.ibm;2.3. import java.io.*;4. import java.nio.*;5. import java.nio.channels.*;6.7. public class CopyFile {8. static public void main(String args[]) throws Exception {9.10. String infile = "c://test/nio_copy.txt";11. String outfile = "c://test/result.txt";12.13. FileInputStream fin = new FileInputStream(infile);14. FileOutputStream fout = new FileOutputStream(outfile);15. // 获取读的通道16. FileChannel fcin = fin.getChannel();17. // 获取写的通道18. FileChannel fcout = fout.getChannel();19. // 定义缓冲区,并指定⼤⼩20. ByteBuffer buffer = ByteBuffer.allocate(1024);21.22. while (true) {23. // 清空缓冲区24. buffer.clear();25. //从通道读取⼀个数据到缓冲区26. int r = fcin.read(buffer);27. //判断是否有从通道读到数据28. if (r == -1) {29. break;30. }31. //将buffer指针指向头部32. buffer.flip();33. //把缓冲区数据写⼊通道34. fcout.write(buffer);35. }36. }37. }缓冲区主要是三个变量positionlimitcapacity这三个变量⼀起可以跟踪缓冲区的状态和它所包含的数据。
JavaNIO编程(⼀)NIO⼊门Java NIO 基本介绍Java NIO 全称 java non-blocking IO,是指 JDK 提供的新 API。
从 JDK1.4 开始,Java 提供了⼀系列改进的输⼊/输出的新特性,被统称为 NIO(即 New IO),是同步⾮阻塞的NIO 相关类都被放在 java.nio 包及⼦包下,并且对原 java.io 包中的很多类进⾏改写。
【基本案例】NIO 有三⼤核⼼部分:Channel(通道),Buffer(缓冲区), Selector(选择器)NIO是⾯向缓冲区,或者⾯向块编程的。
数据读取到⼀个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使⽤它可以提供⾮阻塞式的⾼伸缩性⽹络Java NIO的⾮阻塞模式,使⼀个线程从某通道发送请求或者读取数据,但是它仅能得到⽬前可⽤的数据,如果⽬前没有数据可⽤时,就什么都不会获取,⽽不是保持线程阻塞,所以直⾄数据变的可以读取之前,该线程可以继续做其他的事情。
⾮阻塞写也是如此,⼀个线程请求写⼊⼀些数据到某通道,但不需要等待它完全写⼊,这个线程同时可以去做别的事情。
通俗理解:NIO是可以做到⽤⼀个线程来处理多个操作的。
假设有10000个请求过来,根据实际情况,可以分配50或者100个线程来处理。
不像之前的阻塞IO那样,⾮得分配10000个。
HTTP2.0使⽤了多路复⽤的技术,做到同⼀个连接并发处理多个请求,⽽且并发请求的数量⽐HTTP1.1⼤了好⼏个数量级。
NIO 和 BIO 的⽐较BIO 以流的⽅式处理数据,⽽ NIO 以块的⽅式处理数据,块 I/O 的效率⽐流 I/O ⾼很多BIO 是阻塞的,NIO 则是⾮阻塞的BIO基于字节流和字符流进⾏操作,⽽ NIO 基于 Channel(通道)和 Buffer(缓冲区)进⾏操作,数据总是从通道读取到缓冲区中,或者从缓冲区写⼊到通道中。
Selector(选择器)⽤于监听多个通道的事件(⽐如:连接请求,数据到达等),因此使⽤单个线程就可以监听多个客户端通道NIO 三⼤核⼼类库简单介绍Selector 、 Channel 和 Buffer 的关系图每个 Channel 都会对应⼀个 Buffer,读写数据都要经过 BufferSelector 对应⼀个线程,⼀个线程可以对应多个 Channel(连接)该图反应了有三个 Channel 注册到该 Selector 多路复⽤器上Selector 会不断地轮询注册在其上地 Channel,如果某个Channel发送读或者写事件就会被轮询出来Buffer 就是⼀个内存块,底层是有⼀个数组,但 Buffer 不仅仅是⼀个数组,还提供了对数据的结构化访问以及维护读写位置等信息数据的读取写⼊是通过Buffer, 这个是和BIO 最⼤的区别, BIO 中要么是输⼊流,要么是输出流, 不能双向;但是NIO的 Buffer 是可以读也可以写, 只需要 flip ⽅法切换channel 是双向的, 可以返回底层操作系统的情况, ⽐如 Linux底层的操作系统通道就是双向的。
JA V A NIO基础知识总结二(一)、回顾一下总结一:(1)、NIO的几个概念:①、Buffer :内存块,实质就是一个数组。
NIO数据读或写得中转地。
②、Channel:连接设备的通道。
用于向buffer提供数据或者读取数据,异步I/O支持。
③、Selector :channel事件的监听者,他能检测到一个或多个通道,并将事件分发出去④、SelectionKey:channel上发生的事件,包含了事件的状态信息和时间以及对应的channel。
(2)、在前面总结一中,最后的时候给出了一个完整的关于NIO操作网络套接字的例子,在这里先总结一下构建基于NIO的服务端的一般步骤:①、构造一个Selector②、打开一个serverSocketChannel③、设定serverSocketChannel为非阻塞④、绑定socketserverChannel到一个主机地址和端口⑤、注册selector并告知感兴趣的事情(3)、Channel的状态有四种:①、Connectable:当一个Channel完成socket连接操作已完成或者已失败。
②、Acceptable:当一个Channel已准备好接受一个新的socket连接时,channel是Acceptale③、Readable:当一个channel能被读时。
④、Writable:当一个Channel能被写时为可写状态。
(4)、下面是NIO中的关系图,来自于《java编程思想》(二)、基于多线程的NIO总结一的例子,是基于单线程的,单线程的好处是简单,不用去考虑过于复杂的线程问题,但是仔细想一下,如果数据在网络传输的过程中发生了阻塞呢,那岂不是要花费很多的时间?再者如果我们要实现像QQ中的聊天室呢,如何实现呢?。
为了解决这些问题,我们现在试着采用多线程的,但是采用多线程,会产生很多线程,创建、销毁线程都是要花费时间的,所以这里可以运用到线程池来管理。
下面一个例子是:客户端发来信息,服务端然后转发所有的信息给在线的客户端。
import java.io.IOException;import .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;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.logging.Level;import java.util.logging.Logger;public class RSocketServer implements Runnable {private final static int POOLSIZE = 100;// 处理线程池的大小private SelectionKey selectionKey; // 选择键private ExecutorService service =Executors.newFixedThreadPool(POOLSIZE);// 固定大小的线程池private boolean isRunning = true;private Selector selector;// 选择器private String writeMsg;// 需要写的信息private ServerSocketChannel ssc;public RSocketServer() {try {selector = Selector.open();ssc = ServerSocketChannel.open();ssc.configureBlocking(false);ssc.socket().bind(new InetSocketAddress(8080));selectionKey = ssc.register(selector,SelectionKey.OP_ACCEPT);System.out.println("服务器启动成功!正在端口为8080上等待...");} catch (Exception e) {e.printStackTrace();}}public void run() {try {while (isRunning) {int num = -1;try {// 监控注册在selector上的SelectableChannelnum = selector.select();} catch (IOException e) {e.printStackTrace();}if (num == 0) {continue;}Iterator<SelectionKey> it =selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();it.remove();if (!key.isValid())continue;if (key.isAcceptable()) {getConn(key);} else if (key.isReadable()) {System.out.println("可读");readMsg(key);}else if (key.isValid() && key.isWritable()) {if (writeMsg != null) {System.out.println("可写");RWriter(key);}}else break;}}Thread.yield();} catch (Exception e) {e.printStackTrace();}}private void getConn(SelectionKey key) throws IOException { ssc = (ServerSocketChannel) key.channel();SocketChannel sc = ssc.accept();sc.configureBlocking(false);sc.register(selector, SelectionKey.OP_READ);}private void readMsg(SelectionKey key) throws IOException {StringBuffer sb = new StringBuffer();SocketChannel sc = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.clear();int len = 0;while ((len = sc.read(buffer)) > 0) {buffer.flip();sb.append(new String(buffer.array(), 0, len));}if (sb.length() > 0)System.out.println("从客户端发来的数据:" + sb.toString());if ("exit".equals(sb.toString().trim())||sb.length()==0) { sc.write(ByteBuffer.wrap("bye".getBytes()));System.out.println("服务端已经关闭");key.cancel();sc.close();sc.socket().close();} else {String msg = sc.socket().getRemoteSocketAddress() + ":"+ sb.toString();Iterator<SelectionKey> it =key.selector().keys().iterator();// 把数据分发到每一个已经连接的客户端while (it.hasNext()) {SelectionKey skey = it.next();if (skey != key && skey != selectionKey) {RWriter myWriter = new RWriter(skey, msg);service.execute(myWriter);}}}}public static void main(String[] args) {RSocketServer server = new RSocketServer();new Thread(server).start();}class RWriter implements Runnable {SelectionKey key;String msg;public RWriter(SelectionKey key, String msg) {this.key = key;this.msg = msg;}public void run() {try {SocketChannel client = (SocketChannel) key.channel();client.write(ByteBuffer.wrap(msg.getBytes()));Thread.yield();} catch (IOException ex) {Logger.getLogger(RWriter.class.getName()).log(Level.SEVERE,null, ex);}}}private void RWriter(SelectionKey key) throws IOException { SocketChannel sc = (SocketChannel) key.channel();String str = (String) key.attachment();sc.write(ByteBuffer.wrap(str.getBytes()));key.interestOps(SelectionKey.OP_READ);}}(三)、Java NIO的Reactor模式看了上面的例子,有没有发觉一些问题呢?会不会觉得例子有点“乱七八糟”的感觉,接收数据、业务逻辑、发送数据、数据的包装等等,这些都需要我们去处理。