暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

NIO-编程实战(二)

IT分享 2020-02-04
193

NIO-关于的网络通信:


1、会 将通道上的关注的事件注册到Selector管家上。

2、然后Selector会返回一个SelectionKey,方便后面进行轮询的。

3、将key自动加入keys的集合中去。

4、关注事件的发生。

5、做完下面的工作以后会专门select()返回,返回一个key,然后去执行对应的事件。

6、然后轮询处理看里面有没有处理相关的事情。做完以后会继续轮询。

只能SelectedKeys当中的一个key会触发事件。读的同时不能去写。

7、当做完的时候从selectedKeys中将它移除掉,如果不移除会重复的去执行这样的事件。

在程序中:首先看server端的代码如下:

  1. import java.io.IOException;

  2. import java.net.InetSocketAddress;

  3. import java.net.ServerSocket;

  4. import java.nio.ByteBuffer;

  5. import java.nio.channels.SelectionKey;

  6. import java.nio.channels.Selector;

  7. import java.nio.channels.ServerSocketChannel;

  8. import java.nio.channels.SocketChannel;

  9. import java.util.Iterator;

  10. import java.util.Set;


  11. /**

  12. * 服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)

  13. * 客户端连接服务端事件 SelectionKey.OP_CONNECT(8)

  14. * 读事件 SelectionKey.OP_READ(1)

  15. * 写事件 SelectionKey.OP_WRITE(4)

  16. *(2)NIO实现原理

  17. * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,

  18. * 该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,

  19. * 如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,

  20. * 阻塞I/O这时会调用read()方法阻塞地读取数据,

  21. * 而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,

  22. * 如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,

  23. * 则处理线程会一直阻塞直到感兴趣的事件到达为止。

  24. */

  25. public class NIOServer {


  26. /*标识数字*/

  27. private int flag = 0;

  28. /*缓冲区大小*/

  29. private int BLOCK = 4096;

  30. /*接受数据缓冲区*/

  31. private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);

  32. /*发送数据缓冲区*/

  33. private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);

  34. private Selector selector;


  35. public NIOServer(int port) throws IOException {

  36. // 打开服务器套接字通道

  37. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

  38. // 服务器配置为非阻塞,默认为阻塞的。

  39. serverSocketChannel.configureBlocking(false);

  40. // 检索与此通道关联的服务器套接字

  41. ServerSocket serverSocket = serverSocketChannel.socket();

  42. // 进行服务的绑定

  43. serverSocket.bind(new InetSocketAddress(port));

  44. // 通过open()方法找到Selector

  45. selector = Selector.open();

  46. // 注册到selector,等待连接

  47. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

  48. System.out.println("Server Start----8888:");

  49. }



  50. // 监听

  51. private void listen() throws IOException {

  52. // 轮询IO事件

  53. while (true) {

  54. System.out.println("=====");

  55. // 选择一组键,并且相应的通道已经打开

  56. //此方法执行处于阻塞模式的选择操作。

  57. selector.select();

  58. System.out.println("===+++==");

  59. // 返回此选择器的已选择键集。

  60. Set<SelectionKey> selectionKeys = selector.selectedKeys();

  61. Iterator<SelectionKey> iterator = selectionKeys.iterator();

  62. while (iterator.hasNext()) {

  63. SelectionKey selectionKey = iterator.next();

  64. iterator.remove();

  65. handleKey(selectionKey);

  66. }

  67. }

  68. }


  69. // 处理请求

  70. private void handleKey(SelectionKey selectionKey) throws IOException {

  71. // 接受请求

  72. ServerSocketChannel server = null;

  73. SocketChannel client = null;

  74. String receiveText;

  75. String sendText;

  76. int count=0;

  77. // 测试此键的通道是否已准备好接受新的套接字连接。

  78. if (selectionKey.isAcceptable()) {

  79. System.out.println("_++++++");

  80. // 返回为之创建此键的通道。

  81. server = (ServerSocketChannel) selectionKey.channel();

  82. // 接受到此通道套接字的连接。

  83. // 此方法返回的套接字通道(如果有)将处于阻塞模式。

  84. client = server.accept();

  85. System.out.println("=============");

  86. // 配置为非阻塞

  87. client.configureBlocking(false);

  88. // 注册到selector,等待连接

  89. client.register(selector, SelectionKey.OP_READ);

  90. } else if (selectionKey.isReadable()) {

  91. // 返回为之创建此键的通道。

  92. client = (SocketChannel) selectionKey.channel();

  93. //将缓冲区清空以备下次读取

  94. receivebuffer.clear();

  95. //读取服务器发送来的数据到缓冲区中

  96. count = client.read(receivebuffer);

  97. if (count > 0) {

  98. receiveText = new String( receivebuffer.array(),0,count);

  99. System.out.println("服务器端接受客户端数据--:"+receiveText);

  100. client.register(selector, SelectionKey.OP_WRITE);

  101. }

  102. } else if (selectionKey.isWritable()) {

  103. //将缓冲区清空以备下次写入

  104. sendbuffer.clear();

  105. // 返回为之创建此键的通道。

  106. client = (SocketChannel) selectionKey.channel();

  107. sendText="message from server--" + flag++;

  108. //向缓冲区中输入数据

  109. sendbuffer.put(sendText.getBytes());

  110. //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

  111. sendbuffer.flip();

  112. //输出到通道

  113. client.write(sendbuffer);

  114. System.out.println("服务器端向客户端发送数据--:"+sendText);

  115. client.register(selector, SelectionKey.OP_READ);

  116. }

  117. }


  118. /**

  119. * @param args

  120. * @throws IOException

  121. */

  122. public static void main(String[] args) throws IOException {

  123. int port = 8888;

  124. NIOServer server = new NIOServer(port);

  125. server.listen();

  126. }

  127. }

运行的结果如下:

客户端的代码如下:

  1. import java.io.IOException;

  2. import java.net.InetSocketAddress;

  3. import java.nio.ByteBuffer;

  4. import java.nio.channels.SelectionKey;

  5. import java.nio.channels.Selector;

  6. import java.nio.channels.SocketChannel;

  7. import java.util.Iterator;

  8. import java.util.Set;


  9. public class NIOClient {


  10. /*标识数字*/

  11. private static int flag = 0;

  12. /*缓冲区大小*/

  13. private static int BLOCK = 4096;

  14. /*接受数据缓冲区*/

  15. private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);

  16. /*发送数据缓冲区*/

  17. private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);

  18. /*服务器端地址*/

  19. private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(

  20. "localhost", 8888);


  21. public static void main(String[] args) throws IOException {

  22. // 打开socket通道

  23. SocketChannel socketChannel = SocketChannel.open();

  24. // 设置为非阻塞方式

  25. socketChannel.configureBlocking(false);

  26. // 打开选择器

  27. Selector selector = Selector.open();

  28. // 注册连接服务端socket动作

  29. socketChannel.register(selector, SelectionKey.OP_CONNECT);

  30. // 连接

  31. socketChannel.connect(SERVER_ADDRESS);

  32. // 分配缓冲区大小内存


  33. Set<SelectionKey> selectionKeys;

  34. Iterator<SelectionKey> iterator;

  35. SelectionKey selectionKey;

  36. SocketChannel client;

  37. String receiveText;

  38. String sendText;

  39. int count=0;


  40. while (true) {

  41. //选择一组键,其相应的通道已为 I/O 操作准备就绪。

  42. //此方法执行处于阻塞模式的选择操作。

  43. selector.select();

  44. //返回此选择器的已选择键集。

  45. selectionKeys = selector.selectedKeys();

  46. //System.out.println(selectionKeys.size());

  47. iterator = selectionKeys.iterator();

  48. while (iterator.hasNext()) {

  49. selectionKey = iterator.next();

  50. if (selectionKey.isConnectable()) {

  51. System.out.println("client connect");

  52. client = (SocketChannel) selectionKey.channel();

  53. // 判断此通道上是否正在进行连接操作。

  54. // 完成套接字通道的连接过程。

  55. if (client.isConnectionPending()) {

  56. client.finishConnect();

  57. System.out.println("完成连接!");

  58. sendbuffer.clear();

  59. sendbuffer.put("Hello,Server".getBytes());

  60. sendbuffer.flip();

  61. client.write(sendbuffer);

  62. }

  63. client.register(selector, SelectionKey.OP_READ);

  64. } else if (selectionKey.isReadable()) {

  65. client = (SocketChannel) selectionKey.channel();

  66. //将缓冲区清空以备下次读取

  67. receivebuffer.clear();

  68. //读取服务器发送来的数据到缓冲区中

  69. count=client.read(receivebuffer);

  70. if(count>0){

  71. receiveText = new String( receivebuffer.array(),0,count);

  72. System.out.println("客户端接受服务器端数据--:"+receiveText);

  73. client.register(selector, SelectionKey.OP_WRITE);

  74. }


  75. } else if (selectionKey.isWritable()) {

  76. sendbuffer.clear();

  77. client = (SocketChannel) selectionKey.channel();

  78. sendText = "message from client--" + (flag++);

  79. sendbuffer.put(sendText.getBytes());

  80. //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

  81. sendbuffer.flip();

  82. client.write(sendbuffer);

  83. System.out.println("客户端向服务器端发送数据--:"+sendText);

  84. client.register(selector, SelectionKey.OP_READ);

  85. }

  86. }

  87. selectionKeys.clear();

  88. }

  89. }

  90. }

运行的结果如下:一直循环和打印相应的数据.

革命尚未成功,继续努力!!!!。

如果觉的哪里不好的话,请各位大佬指出缺点,互相学习

文章转载自IT分享,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论