`
lovnet
  • 浏览: 6691155 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

NIO:Selector 类用法

 
阅读更多

如本章第1节中提到的,Selector类可用于避免使用非阻塞式客户端中很浪费资源的"忙等"方法。例如,考虑一个即时消息服务器。可能有上千个客户端同时连接到了服务器,但在任何时刻都只有非常少量的(甚至可能没有)消息需要读取和分发。这就需要一种方法阻塞等待,直到至少有一个信道可以进行I/O操作,并指出是哪个信道。NIO的选择器就实现了这样的功能。一个Selector实例可以同时检查(如果需要,也可以等待)一组信道的I/O状态。用专业术语来说,选择器就是一个多路开关选择器,因为一个选择器能够管理多个信道上的I/O操作。

要使用选择器,需要创建一个Selector实例(使用静态工厂方法open())并将其注册register)到想要监控的信道上(注意,这要通过channel的方法实现,而不是使用selector的方法)。最后,调用选择器的select()方法。该方法会阻塞等待,直到有一个或更多的信道准备好了I/O操作或等待超时。select()方法将返回可进行I/O操作的信道数量。现在,在一个单独的线程中,通过调用select()方法就能检查多个信道是否准备好进行I/O操作。如果经过一段时间后仍然没有信道准备好,select()方法就返回0,并允许程序继续执行其他任务。

下面来看一个例子。假设我们想要使用信道和选择器来实现一个回显服务器,并且不使用多线程和忙等。为了使不同协议都能方便地使用这个基本的服务模式,我们把信道中与具体协议相关的处理各种I/O的操作(接收,读,写)分离了出来。TCPProtocol定义了通用TCPSelectorServer类与特定协议之间的接口,包括三个方法,每个方法代表了一种I/O型式。当有信道准备好I/O操作时,服务器只需要调用相应的方法即可。

TCPProtocol.java

0 import java.nio.channels.SelectionKey;

1 import java.io.IOException;

2

3 public interface TCPProtocol {

4 void handleAccept(SelectionKey key) throws IOException;

5 void handleRead(SelectionKey key) throws IOException;

6 void handleWrite(SelectionKey key) throws IOException;

7 }

TCPProtocol.java

在服务器端创建一个选择器,并将其与每个侦听客户端连接的套接字所对应的ServerSocketChannel注册在一起。然后进行反复循环,调用select()方法,并调用相应的操作器例程对各种类型的I/O操作进行处理。

TCPServerSelector.java

0 import java.io.IOException;

1 import java.net.InetSocketAddress;

2 import java.nio.channels.SelectionKey;

3 import java.nio.channels.Selector;

4 import java.nio.channels.ServerSocketChannel;

5 import java.util.Iterator;

6

7 public class TCPServerSelector {

8

9 private static final int BUFSIZE = 256; // Buffer size

(bytes)

10 private static final int TIMEOUT = 3000; // Wait timeout

(milliseconds)

11

12 public static void main(String[] args) throws

IOException {

13

14 if (args.length < 1) { // Test for correct # of args

15 throw new IllegalArgumentException("Parameter(s):

<Port> ...");

16 }

17

18 // Create a selector to multiplex listening sockets and

connections

19 Selector selector = Selector.open();

20

21 // Create listening socket channel for each port and

register selector

22 for (String arg : args) {

23 ServerSocketChannel listnChannel =

ServerSocketChannel.open();

24 listnChannel.socket().bind(new

InetSocketAddress(Integer.parseInt(arg)));

25 listnChannel.configureBlocking(false); // must be

nonblocking to register

26 // Register selector with channel. The returned key is

ignored

27 listnChannel.register(selector,

SelectionKey.OP_ACCEPT);

28 }

29

30 // Create a handler that will implement the protocol

31 TCPProtocol protocol = new

EchoSelectorProtocol(BUFSIZE);

32

33 while (true) { // Run forever, processing available I/O

operations

34 // Wait for some channel to be ready (or timeout)

35 if (selector.select(TIMEOUT) == 0) { // returns # of

ready chans

36 System.out.print(".");

37 continue;

38 }

39

40 // Get iterator on set of keys with I/O to process

41 Iterator<SelectionKey> keyIter =

selector.selectedKeys().iterator();

42 while (keyIter.hasNext()) {

43 SelectionKey key = keyIter.next(); // Key is bit mask

44 // Server socket channel has pending connection

requests?

45 if (key.isAcceptable()) {

46 protocol.handleAccept(key);

47 }

48 // Client socket channel has pending data?

49 if (key.isReadable()) {

50 protocol.handleRead(key);

51 }

52 // Client socket channel is available for writing and

53 // key is valid (i.e., channel not closed)?

54 if (key.isValid() && key.isWritable()) {

55 protocol.handleWrite(key);

56 }

57 keyIter.remove(); // remove from set of selected keys

58 }

59 }

60 }

61 }

TCPServerSelector.java

1.设置:第14-19

验证至少有一个参数,创建一个Selector实例。

2.为每个端口创建一个ServerSocketChannel:第22-28

创建一个ServerSocketChannel实例:23

使其侦听给定端口:第24

需要获得底层的ServerSocket,并以端口号作为参数调用其bind()方法。任何超出适当数值范围的参数都将导致抛出IOException异常。

配置为非阻塞模式:第25

只有非阻塞信道才可以注册选择器,因此需要将其配置为适当的状态。

为信道注册选择器:第27

在注册过程中指出该信道可以进行"accept"操作。

3.创建协议操作器:第31

为了访问回显协议中的操作方法,创建了一个EchoSelectorProtocol实例。该实例包含了需要用到的方法。

4.反复循环,等待I/O,调用操作器:第33-59

选择:第35

这个版本的select()方法将阻塞等待,直到有准备好I/O操作的信道,或直到发生了超时。该方法将返回准备好的信道数。返回0表示超时,这时程序将打印一个点来标记经过的时间和迭代次数。

获取所选择的键集:第41

调用selectedKeys()方法返回一个Set实例,并从中获取一个Iterator。该集合中包含了每个准备好某一I/O操作的信道的SelectionKey(在注册时创建)。

在键集上迭代,检测准备好的操作:第42-58

对于每个键,检查其是否准备好进行accep()操作,是否可读或可写,并调用相应的操作器方法对每种情况进行指定的操作。

从集合中移除键:第57

由于select()操作只是向Selector所关联的键集合中添加元素,因此,如果不移除每个处理过的键,它就会在下次调用select()方法是仍然保留在集合中,而且可能会有无用的操作来调用它。

TCPServerSelector的大部分内容都与协议无关,只有协议赋值那一行代码是针对的特定协议。所有协议细节都包含在了TCPProtocol接口的具体实现中。EchoSelectorProtocol类就实现了该回显协议的操作器。你可以轻松地为自其他协议编写自己的操作器,或在我们的回显协议操作器上进行改进。

EchoSelectorProtocol.java

0 import java.nio.channels.SelectionKey;

1 import java.nio.channels.SocketChannel;

2 import java.nio.channels.ServerSocketChannel;

3 import java.nio.ByteBuffer;

4 import java.io.IOException;

5

6 public class EchoSelectorProtocol implements

TCPProtocol {

7

8 private int bufSize; // Size of I/O buffer

9

10 public EchoSelectorProtocol(int bufSize) {

11 this.bufSize = bufSize;

12 }

13

14 public void handleAccept(SelectionKey key) throws

IOException {

15 SocketChannel clntChan = ((ServerSocketChannel)

key.channel()).accept();

16 clntChan.configureBlocking(false); // Must be

nonblocking to register

17 // Register the selector with new channel for read and

attach byte buffer

18 clntChan.register(key.selector(), SelectionKey.

OP_READ, ByteBuffer.allocate(bufSize));

19

20 }

21

22 public void handleRead(SelectionKey key) throws

IOException {

23 // Client socket channel has pending data

24 SocketChannel clntChan = (SocketChannel)

key.channel();

25 ByteBuffer buf = (ByteBuffer) key.attachment();

26 long bytesRead = clntChan.read(buf);

27 if (bytesRead == -1) { // Did the other end close?

28 clntChan.close();

29 } else if (bytesRead > 0) {

30 // Indicate via key that reading/writing are both of

interest now.

31 key.interestOps(SelectionKey.OP_READ |

SelectionKey.OP_WRITE);

32 }

33 }

34

35 public void handleWrite(SelectionKey key) throws

IOException {

36 /*

37 * Channel is available for writing, and key is valid

(i.e., client channel

38 * not closed).

39 */

40 // Retrieve data read earlier

41 ByteBuffer buf = (ByteBuffer) key.attachment();

42 buf.flip(); // Prepare buffer for writing

43 SocketChannel clntChan = (SocketChannel)

key.channel();

44 clntChan.write(buf);

45 if (!buf.hasRemaining()) { // Buffer completely

written?

46 // Nothing left, so no longer interested in writes

47 key.interestOps(SelectionKey.OP_READ);

48 }

49 buf.compact(); // Make room for more data to be read

in

50 }

51

52 }32

EchoSelectorProtocol.java

1.声明实现TCPProtocol接口:第6

2.成员变量和构造函数:第8-12

每个实例都包含了将要为每个客户端信道创建的缓冲区大小。

3. handleAccept():第14-20

从键中获取信道,并接受连接:第15

channel()方法返回注册时用来创建键的Channel。(我们知道该Channel是一个ServerSocketChannel,因为这是我们注册的惟一一种支持"accept"操作的信道。)accept()法为传入的连接返回一个SocketChannel实例。

设置为非阻塞模式:第16

再次提醒,这里无法注册阻塞式信道。

为信道注册选择器:第18-19

可以通过SelectionKey类的selector()方法来获取相应的Selector。我们根据指定大小创建了一个新的ByteBuffer实例,并将其作为参数传递给register()方法。它将作为附件,与register()方法所返回的SelectionKey实例相关联。(在此我们忽略了返回的键,但当信道准备好读数据的I/O操作时,可以通过选出的键集对其进行访问。)

4. handleRead():第22-33

获取键关联的信道:第24

根据其支持数据读取操作可知,这是一个SocketChannel

获取键关联的缓冲区:第25

连接建立后,有一个ByteBuffer附加到该SelectionKey实例上。

从信道中读数据:第27

检查数据流的结束并关闭信道:第27-28

如果read()方法返回-1,则表示底层连接已经关闭,此时需要关闭信道。关闭信道时,将从选择器的各种集合中移除与该信道关联的键。

如果接收完数据,将其标记为可写:第29-31

注意,这里依然保留了信道的可读操作,虽然缓冲区中可能已经没有剩余空间了。

5. handleWrite():第35-50

获取包含数据的缓冲区:第41

附加到SelectionKey上的ByteBuffer包含了之前从信道中读取的数据。

准备缓冲区的写操作:第42

Buffer的内部状态指示了在哪里放入下一批数据,以及缓冲区还剩多少空间。flip()方法用来修改缓冲区的内部状态,以指示write()操作从什么地方获取数据,以及还有剩余多少数据。(下一章将对其进行详细介绍。)该方法的作用是使写数据的操作开始消耗由读操作产生的数据。

获取信道:第43

向信道写数据:第44

如果缓冲区为空,则标记为不再写数据:第45-48

如果缓冲区中之前接收的数据已经没有剩余,则修改该键关联的操作集,指示其只能进行读操作。

压缩缓冲区:第49

如果缓冲区中还有剩余数据,该操作则将其移动到缓冲区的前端,以使下次迭代能够读入更多的数据(第5.4.5节将对这个操作的语义进行详细介绍)。在任何情况下,该操作都将重置缓冲区的状态,因此缓冲区又变为可读。注意,除了在handleWrite()方法内部,与信道关联的缓冲区始终是设置为可读的。

现在我们已经准备好对三大NIO 抽象的细节进行深入研究了。

相关下载:

Java_TCPIP_Socket编程(doc)

http://download.csdn.net/detail/undoner/4940239

文献来源:

UNDONER(小杰博客) :http://blog.csdn.net/undoner

LSOFT.CN(琅软中国) :http://www.lsoft.cn

分享到:
评论

相关推荐

    java selector类的用法举例

    java nio中selector选择器的使用说明文档

    Java NIO Selector用法详解【含多人聊天室实例】

    主要介绍了Java NIO Selector用法,结合实例形式分析了Java NIO Selector基本功能、原理与使用方法,并结合了多人聊天室实例加以详细说明,需要的朋友可以参考下

    Java NIO原理和使用

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能...通过仔细阅读这个例程,相信你已经大致了解NIO的原理和使用方法,下一篇,我们将使用多线程来处理这些数据,再搭建一个自己的Reactor模式。

    浅谈java中nio的使用方式

    NIO其核心概念包括Channel,Selector,SelectionKey,Buffer.

    Java NIO 网络编程初探

    NIO同样拥有文件读写,网络通信等IO操作,今天我们来看看NIO中的TCP网络通信的使用方法。 2. Java NIO 三大核心 Java NIO 有三大核心要素:Channel、Buffer和Selector。Java IO 的操作都是基于输入输出流的,而NIO则...

    精通并发与netty 无加密视频

    第35讲:Java NIO核心类源码解读与分析 第36讲:文件通道用法详解 第37讲:Buffer深入详解 第38讲:NIO堆外内存与零拷贝深入讲解 第39讲:NIO中Scattering与Gathering深度解析 第40讲:Selector源码深入分析 ...

    NIO实现网络聊天室

    Selector常用方法: public static Selector Open(); 得到一个选择器对象 public int select(long timeout); 监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集 合中,参数是一...

    send_file:通过Java https发送文件服务器和客户端

    send_file Introduce一个使用 NIO + selector + send file 技术的 server + client ,专门用于服务器之间搬运文件。quick start打开 example module src 目录.运行 example.ServerDemo运行 example.ClientDemo注意: ...

    java8看不到源码-awesome-articles:不同主题的文章精选列表

    java8 看不到源码不同 IT 主题的精选文章列表 Java Java8 - Java8 特性的高级解释 - ...类加载器、JVM ...而不是一个线程每个连接的方法 - 优秀的视频系列,从每个连接的简单线程到完整的 NIO 服务器,通过对

    精通并发与 netty 视频教程(2018)视频教程

    32_IO体系架构系统回顾与装饰模式的具体应用 33_Java NIO深入详解与体系分析 34_Buffer中各重要状态属性的含义与关系图解 35_Java NIO核心类源码解读与分析 36_文件通道用法详解 37_Buffer深入详解 38_NIO堆外内存与...

    网站开发基础知识

    NioSocket中服务端的处理过程可以...3) 调用Selector的select方法等待请求 4) Selector接收到请求后使用selectedKeys返回SelectionKey集合 5) 使用SelectionKey 获取Channel、Selector 和操作类型并进行操作。

    互联网大厂Netty网络编程开发三部曲-Netty优化+进阶+ 入门 Netty协议设计与解析

    ├─(4) 第1章_04_nio三大组件-服务器设计-selector版.mp4 ├─(5) 第1章_05_bytebuffer-基本使用.mp4 ├─(6) 第1章_06_bytebuffer-内部结构.mp4 ├─(7) 第1章_07_bytebuffer-方法演示1.mp4 ├─(8) 第1章_08_...

    精通并发与netty视频教程(2018)视频教程

    35_Java NIO核心类源码解读与分析 36_文件通道用法详解 37_Buffer深入详解 38_NIO堆外内存与零拷贝深入讲解 39_NIO中Scattering与Gathering深度解析 40_Selector源码深入分析 41_NIO网络访问模式分析 42_NIO网络编程...

    ieda+netty.zip

    class java.nio.channels.Selector 是 Java 的非阻塞 I/O 实现的关键。它使用了事件通知 API 以确定在一组非阻塞套接字中有哪些已经就绪能够进 行 I/O 相关的操作。继续上面的例子,就是当有人输入了 通过Selector ...

    平安科技java笔试题100道-com.tbaldridge.hermod:基于邮箱的分布式计算库

    平安科技java笔试题100道赫莫德 这是一个旨在回答“如何轻松地将数据从一个 JVM 上的运行发送到另一个 JVM”的问题的库 这个库中包含的类比是邮箱的概念。...单个线程使用 NIO Selector 来监视系统管理的

    Java CP/IP Socket编程

    JAVA SOCKET 编程的经典之书,(中文版)里面的代码可直接复制使用! 目录: 第1章简介..........3 1.1 计算机网络,分组报文和协议..........3 1.2 关于地址..........6 1.3 关于名字..........8 1.4 客户端...

    java核心知识点整理.pdf

    本地方法区(线程私有) ................................................................................................................ 23 2.2.4. 堆(Heap-线程共享)-运行时数据区 ...........................

Global site tag (gtag.js) - Google Analytics