选择器服务器端代码

上一篇文章毫无条理地讲了很多和选择器相关的知识点,下面进入实战,看一下如何写和使用选择器实现服务端Socket数据接收的程序,这也是NIO中最核心、最精华的部分。

看一下代码:

1 public classSelectorServer

2 {

3 private static int PORT = 1234;

4

5 public static void main(String[] args) throwsException

6 {

7 //先确定端口号

8 int port =PORT;

9 if (args != null && args.length > 0)

10 {

11 port = Integer.parseInt(args[0]);

12 }

13 //打开一个ServerSocketChannel

14 ServerSocketChannel ssc =ServerSocketChannel.open();

15 //获取ServerSocketChannel绑定的Socket

16 ServerSocket ss =ssc.socket();

17 //设置ServerSocket监听的端口

18 ss.bind(newInetSocketAddress(port));

19 //设置ServerSocketChannel为非阻塞模式

20 ssc.configureBlocking(false);

21 //打开一个选择器

22 Selector selector =Selector.open();

23 //将ServerSocketChannel注册到选择器上去并监听accept事件

24 ssc.register(selector, SelectionKey.OP_ACCEPT);

25 while (true)

26 {

27 //这里会发生阻塞,等待就绪的通道

28 int n =selector.select();

29 //没有就绪的通道则什么也不做

30 if (n == 0)

31 {

32 continue;

33 }

34 //获取SelectionKeys上已经就绪的通道的集合

35 Iterator iterator =selector.selectedKeys().iterator();

36 //遍历每一个Key

37 while(iterator.hasNext())

38 {

39 SelectionKey sk =iterator.next();

40 //通道上是否有可接受的连接

41 if(sk.isAcceptable())

42 {

43 ServerSocketChannel ssc1 =(ServerSocketChannel)sk.channel();

44 SocketChannel sc =ssc1.accept();

45 sc.configureBlocking(false);

46 sc.register(selector, SelectionKey.OP_READ);

47 }

48 //通道上是否有数据可读

49 else if(sk.isReadable())

50 {

51 readDataFromSocket(sk);

52 }

53 iterator.remove();

54 }

55 }

56 }

57

58 private static ByteBuffer bb = ByteBuffer.allocate(1024);

59

60 //从通道中读取数据

61 protected static void readDataFromSocket(SelectionKey sk) throwsException

62 {

63 SocketChannel sc =(SocketChannel)sk.channel();

64 bb.clear();

65 while (sc.read(bb) > 0)

66 {

67 bb.flip();

68 while(bb.hasRemaining())

69 {

70 System.out.print((char)bb.get());

71 }

72 System.out.println();

73 bb.clear();

74 }

75 }

76 }

代码中已经有了相关的注释,这里继续解释一下:

(1)第8行~第12行,确定要监听的端口号,这里是1234

(2)第13行~第20行,由于选择器管理的是通道(Channel),因此首先 要有通道。这里是服务器的程序,因此获取ServerSocketChannel,同时获取它所对应的ServerSocket,设置服务端的 Channel为非阻塞模式,并绑定之前确定的端口号1234

(3)第21行~第24行,打开一个选择器,并注册当前通道感兴趣的时间为accept时间,即监听来自客户端的Socket数据

(4)第25行~第28行,调用select()方法等待来自客户端的Socket数据。程序会阻塞在这儿不会往下走,直到客户端有Socket数据的到来为止,所以严格意义上来说,NIO并不是一种非阻塞IO,因为NIO会阻塞在Selector的select()方法上

(5)第29行~第33行,没有什么好说的,如果select()方法获取的数据是0的话,下面的代码都没必要走,当然这是有可能发生的

(6)第34行~第39行,获取到已经就绪的通道的迭代器进行迭代,泛型是选择键SelectionKey,前文讲过,选择键用于封装特定的通道

(7)第40行~第52行,这里是一个关键点、核心点,这里做了两件事情:

a)满足isAcceptable()则表示该通道上有数据到来了,此时我们做的事情不是获取该通道->创建一个线程来读取该通道上的数据,这么做就和前面一直讲的阻塞IO没有区别了,也无法发挥出NIO的优势来。我们做的事情只是简单地将对应的SocketChannel注册到选择器上,通过传入OP_READ标记,告诉选择器我们关心新的Socket通道什么时候可以准备好读数据

b)满足isReadable()则表示新注册的Socket通道已经可以读取数据了,此时调用readDataFromSocket方法读取SocketChannel中的数据,读取数据的方法前面通道的文章中已经详细讲过了,就不讲了

(8)第53行,将键移除,这一行很重要也是容易忘记的一步操作。加入不remove,将会导致45行中出现空指针异常,原因不难理解,可以自己思考一下。

选择器客户端代码

选择器客户端的代码,没什么要求,只要向服务器端发送数据就可以了。这里选用的是Java NIO4:Socket通道一文中,最后一部分开五个线程向服务端发送数据的程序:

1 public classSelectorClient

2 {

3 private static final String STR = "Hello World!";

4 private static final String REMOTE_IP = "127.0.0.1";

5 private static final int THREAD_COUNT = 5;

6

7 private static class NonBlockingSocketThread extendsThread

8 {

9 public voidrun()

10 {

11 try

12 {

13 int port = 1234;

14 SocketChannel sc =SocketChannel.open();

15 sc.configureBlocking(false);

16 sc.connect(newInetSocketAddress(REMOTE_IP, port));

17 while (!sc.finishConnect())

18 {

19 System.out.println("同" + REMOTE_IP + "的连接正在建立,请稍等!");

20 Thread.sleep(10);

21 }

22 System.out.println("连接已建立,待写入内容至指定ip+端口!时间为" +System.currentTimeMillis());

23 String writeStr = STR + this.getName();

24 ByteBuffer bb =ByteBuffer.allocate(writeStr.length());

25 bb.put(writeStr.getBytes());

26 bb.flip(); //写缓冲区的数据之前一定要先反转(flip)

27 sc.write(bb);

28 bb.clear();

29 sc.close();

30 }

31 catch(IOException e)

32 {

33 e.printStackTrace();

34 }

35 catch(InterruptedException e)

36 {

37 e.printStackTrace();

38 }

39 }

40 }

41

42 public static void main(String[] args) throwsException

43 {

44 NonBlockingSocketThread[] nbsts = newNonBlockingSocketThread[THREAD_COUNT];

45 for (int i = 0; i < THREAD_COUNT; i++)

46 nbsts[i] = newNonBlockingSocketThread();

47 for (int i = 0; i < THREAD_COUNT; i++)

48 nbsts[i].start();

49 //一定要join保证线程代码先于sc.close()运行,否则会有AsynchronousCloseException

50 for (int i = 0; i < THREAD_COUNT; i++)

51 nbsts[i].join();

52 }

53 }

代码执行结果

先运行服务端程序:

空白,很正常,因为在监听客户端数据的到来,此时并没有数据。接着运行客户端程序:

看到5个线程的数据已经发送,此时服务端的执行情况是:

数据全部接收到并打印,看到右边的方框还是红色的,说明这5个线程的数据接收、打印完毕之后,再继续等待着客户端的数据的到来。

总结一下Selector的执行两个关键点:

1、注册一个ServerSocketChannel到selector中,这个通道的作用只是为了监听客户端是否有数据到来(这里注意一下有数据到来,意思是假如需要接收100个字节,如果到来了1个字节就算数据到来了),只要有数据到来,就把特定通道注册到selector中,并指定其事件为读事件。

2、ServerSocketChannel和SocketChannel(通道 里面的是客户端的数据)共同存在在Selector中,只要有注册的事件到来,Selector取消阻塞状态,遍历SelectionKey集合,继续注 册读取数据的通道,或者是从通道中读取数据。

选择过程的可扩展性

从上面的代码以及之前对于Selector的解读可以看到,Selector可以简化用单线程同时管理多个可选择通道的实现。使用一个线程来为多个通道提供服务,通过消除管理各个线程的额外开销,可能会降低复杂性并可能大幅提升性能。但只使用一个线程来服务所有可选择的通道是不是一个好主意呢?这要看情况。

对单核CPU的系统而言这可能是一个好主意,因为在任何情况下都只有一个线程能够 运行。通过消除在线程之间进行上下文切换带来的额外开销,总吞吐量可以提高。但对于一个多核CPU的系统而言呢?字啊一个有n个CPU的系统上,当一个单 一的线程线性轮流地处理每一个线程时,可能有(n-1)个CPU处于空闲状态。

一种可行的解决办法是使用多个选择器。但是请尽量不要这么做,在大量通道上执行就绪选择并不会有很大的开销,大多数工作是由底层操作系统完成的,管理多个选择器并随机地将通道分派给它们当中的一个并不是这个问题的合理的解决方案。

一种更好的解决方案是对所有的可选择通道使用同一个选择器,并将对就绪选择通道的服务委托给其他线程。开发者只使用一个线程监控通道的就绪状态,至于通道处于就绪状态之后又如何做,有两种可行的做法:

1、使用一个协调好的工作线程池来处理接收到的数据,当然线程池的大小是可以调整的

2、通道根据功能由不同的工作线程来处理,它们可能是日志线程、命令/控制线程、状态请求线程等

java nio单线程6_Java NIO6:选择器2---代码篇相关推荐

  1. Java NIO(四)选择器Selector

    本文将介绍NIO最重要的部分Selector,并最终完成一个简易的没有图形界面的多人聊天室. 一.简介 Selector像一个注册中心,把各个可选择通道注册在上面. SelectableChannel ...

  2. Java NIO之Selector(选择器)

    **Java高级特性增强-NIO 本部分网络上有大量的资源可以参考,在这里做了部分整理并做了部分勘误,感谢前辈的付出,每节文章末尾有引用列表~ 写在所有文字的前面:作者在此特别推荐Google排名第一 ...

  3. Java NIO系列教程(十二) Java NIO与IO

    原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html 作者:Jakob Jenkov   译者:郭蕾    校对:方腾飞 当学习了Java ...

  4. 快学Java NIO

    Java NIO Tutorial 地址:http://tutorials.jenkov.com/java-nio/index.html Java NIO系列教程译文地址:http://ifeve.c ...

  5. java nio与io_Java NIO和IO的区别(转)

    下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异. 复制代码代码如下: IO                NIO 面向流            面向缓冲 阻塞IO   ...

  6. JAVA NIO基础知识

    本文来说下JAVA NIO基础知识. 文章目录 NIO概述 NIO简介 NIO的特性/NIO与IO区别 读数据和写数据方式: NIO核心组件简单介绍 Java NIO 之 Buffer(缓冲区) Bu ...

  7. 海纳百川而来的一篇相当全面的Java NIO教程

    目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...

  8. Java NIO与IO

    当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景,以及它们如何影响您的代 ...

  9. Java NIO:IO与NIO的区别

    一.概念 NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多.在Java API中提供了两套N ...

最新文章

  1. Android 中 JUnit 测试的配置
  2. 矩阵乘法的四种理解方式
  3. ASP.NET Core教程【一】关于Razor Page的知识
  4. 【pyqt5学习】——进度条progressBar
  5. 三位数除以两位数怎么算竖式_青岛版三年级上册数学5.6三位数除以一位数(最高位商后有余数)微课知识点精讲+练习...
  6. qt样式表中背景图片的使用
  7. win7 找不到 计算机策略组,win7打开组策略报错:找不到资源string.Advanced_EnableSSL3Fallback...
  8. Python与R的争锋:大数据初学者该怎样选?
  9. Python的矩阵分块
  10. 如何解决PDF图片字体未嵌入的问题
  11. cad画计算机网络图,CAD迷你家装五步快速绘制平面图教程
  12. ERP实施顾问职业所具备的知识和能力结构的几个建议
  13. 当代研究生精神状态:早c晚a?!
  14. Ubuntu pptp服务器搭建教程(保姆级)
  15. Axure教程 原型设计工具Axure RP新手入门教程(一):基础
  16. 三菱凌云3故障代码_三菱故障代码表
  17. 用Arduino播放音乐
  18. UVa Problem 10041 Vito’s Family (Vito 家族)
  19. 第一次如何上线项目(一)
  20. list of questions

热门文章

  1. Celery中文翻译-Application
  2. 在Windows7中与虚拟机实现远程桌面连接
  3. linux(gentoo)安装配置conky
  4. TikTok时代细分需求 牛逼亚马逊运营团队打造新爆款
  5. MPLS自身的优点所带来的网络便捷—Vecloud微云
  6. 一种简洁明了地读取文本文件的方法
  7. 筱玛爱游戏——线性基
  8. Linux环境下虚拟环境virtualenv安装和使用(转)
  9. 洛谷 P4175: bzoj 1146: [CTSC2008]网络管理
  10. 学习shell脚本之乘法口诀