NIO-编程实战(二)

简介: NIO-编程实战(二)

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. }

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

相关文章
|
缓存 安全 Java
由浅入深Netty基础知识NIO三大组件原理实战 2
由浅入深Netty基础知识NIO三大组件原理实战
76 0
|
Java
由浅入深Netty基础知识NIO三大组件原理实战 1
由浅入深Netty基础知识NIO三大组件原理实战
103 0
|
2月前
|
Java Linux 应用服务中间件
【编程进阶知识】高并发场景下Bio与Nio的比较及原理示意图
本文介绍了在Linux系统上使用Tomcat部署Java应用程序时,BIO(阻塞I/O)和NIO(非阻塞I/O)在网络编程中的实现和性能差异。BIO采用传统的线程模型,每个连接请求都会创建一个新线程进行处理,导致在高并发场景下存在严重的性能瓶颈,如阻塞等待和线程创建开销大等问题。而NIO则通过事件驱动机制,利用事件注册、事件轮询器和事件通知,实现了更高效的连接管理和数据传输,避免了阻塞和多级数据复制,显著提升了系统的并发处理能力。
67 0
|
3月前
|
缓存 Java Linux
NIO-编程实战(一)
NIO-编程实战(一)
|
5月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解
|
5月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解
|
6月前
|
存储 监控 Java
Java中的NIO编程实践精华
Java中的NIO编程实践精华
|
6月前
|
存储 监控 Java
了解Java中的NIO编程
了解Java中的NIO编程
|
Java
Netty入门到超神系列-Java NIO零拷贝实战
这一章我们来操作一下NIO的零拷贝,这里我会先写代码样式一下传统IO数据拷贝场景下的耗时,然后再对比NIO场景下的考别耗时,通过耗时差异就能看到NIO零拷贝和传统IO拷贝的区别了。
127 0
|
缓存 Java
Java NIO实战篇:使用Socket实现报文交互
Java NIO实战篇:使用Socket实现报文交互
236 0