• Netty学习第六节实例一步学习


    NIO与传统IO对应使用的类:
    ServerSocketChannel相当于ServerSocket
    SocketChannel 相当于Socket
    Selector是NIO的核心类,是负责监听ServerSocketChannel和SocketChannel 
    (所以NIO是可以实现单线程为多个客户端服务的,而传统IO是不可以的)
    SelectionKey 返回的监听结果
     
    原理实例解析:
    比如如果餐厅是一个系统的话,serverSocket相当于一个餐厅的大门,只负责大门的看护(监视),也就是监听端口,客人就是socket,所有客人都需要通过大门进入餐厅,餐厅的服务员就相当于一个线程,传统socket是一个如图的模型(一个服务员服务一个客人):
     
     
    如果NIO也是以一个餐厅系统,它的大门就是ServerSocketChannel ,客户端就是SocketChannel,服务员就是(线程+Selector),其服务员增加了selector能力,即为多个客户服务的能力,同时可以监听大门(欢迎新的客人),如图所示:
     
    Selector.select()============无法看到源码,底层是基于C实现的.
    实例代码:
      1 package com.NIO;
      2 
      3 import java.io.IOException;
      4 import java.net.InetSocketAddress;
      5 import java.nio.ByteBuffer;
      6 import java.nio.channels.SelectionKey;
      7 import java.nio.channels.Selector;
      8 import java.nio.channels.ServerSocketChannel;
      9 import java.nio.channels.SocketChannel;
     10 import java.util.Iterator;
     11 
     12 /**
     13  * NIO服务端
     14  * 
     15  * @author -琴兽-
     16  */
     17 public class NIOServer {
     18     // 通道管理器
     19     private Selector selector;
     20 
     21     /**
     22      * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
     23      * 
     24      * @param port
     25      *            绑定的端口号
     26      * @throws IOException
     27      */
     28     public void initServer(int port) throws IOException {
     29         // 获得一个ServerSocket通道
     30         ServerSocketChannel serverChannel = ServerSocketChannel.open();
     31         // 设置通道为非阻塞
     32         serverChannel.configureBlocking(false);
     33         // 将该通道对应的ServerSocket绑定到port端口
     34         serverChannel.socket().bind(new InetSocketAddress(port));
     35         // 获得一个通道管理器
     36         this.selector = Selector.open();
     37         // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
     38         // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
     39         serverChannel.register(selector, SelectionKey.OP_ACCEPT);
     40     }
     41 
     42     /**
     43      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     44      * 
     45      * @throws IOException
     46      */
     47     public void listen() throws IOException {
     48         System.out.println("服务端启动成功!");
     49         // 轮询访问selector
     50         while (true) {
     51             // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
     52             selector.select();
     53             // 获得selector中选中的项的迭代器,选中的项为注册的事件
     54             Iterator<?> ite = this.selector.selectedKeys().iterator();
     55             while (ite.hasNext()) {
     56                 SelectionKey key = (SelectionKey) ite.next();
     57                 // 删除已选的key,以防重复处理
     58                 ite.remove();
     59 
     60                 handler(key);
     61             }
     62         }
     63     }
     64 
     65     /**
     66      * 处理请求
     67      * 
     68      * @param key
     69      * @throws IOException
     70      */
     71     public void handler(SelectionKey key) throws IOException {
     72         
     73         // 客户端请求连接事件
     74         if (key.isAcceptable()) {
     75             handlerAccept(key);
     76             // 获得了可读的事件
     77         } else if (key.isReadable()) {
     78             handelerRead(key);
     79         }
     80     }
     81 
     82     /**
     83      * 处理连接请求
     84      * 
     85      * @param key
     86      * @throws IOException
     87      */
     88     public void handlerAccept(SelectionKey key) throws IOException {
     89         ServerSocketChannel server = (ServerSocketChannel) key.channel();
     90         // 获得和客户端连接的通道
     91         SocketChannel channel = server.accept();
     92         // 设置成非阻塞
     93         channel.configureBlocking(false);
     94 
     95         // 在这里可以给客户端发送信息哦
     96         System.out.println("新的客户端连接");
     97         // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
     98         channel.register(this.selector, SelectionKey.OP_READ);
     99     }
    100 
    101     /**
    102      * 处理读的事件
    103      * 
    104      * @param key
    105      * @throws IOException
    106      */
    107     public void handelerRead(SelectionKey key) throws IOException {
    108         // 服务器可读取消息:得到事件发生的Socket通道
    109         SocketChannel channel = (SocketChannel) key.channel();
    110         // 创建读取的缓冲区
    111         ByteBuffer buffer = ByteBuffer.allocate(1024);
    112         int read = channel.read(buffer);
    113         if(read > 0){
    114             byte[] data = buffer.array();
    115             String msg = new String(data).trim();
    116             System.out.println("服务端收到信息:" + msg);
    117             
    118             //回写数据
    119             ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());
    120             channel.write(outBuffer);// 将消息回送给客户端
    121         }else{
    122             System.out.println("客户端关闭");
    123             key.cancel();
    124         }
    125     }
    126 
    127     /**
    128      * 启动服务端测试
    129      * 
    130      * @throws IOException
    131      */
    132     public static void main(String[] args) throws IOException {
    133         NIOServer server = new NIOServer();
    134         server.initServer(8000);
    135         server.listen();
    136     }
    137 
    138 }
    View Code
  • 相关阅读:
    Redis接口限流
    CentOS7以下DNS失效问题
    Vue的安装与使用
    使用 REDIS保存SESSION信息
    ThinkPHP 阿里云图片上传
    Laravel6.0+easywechat4.1获取用户授权例子与分享示例
    1.oracle创建本地数据库步骤及导入数据
    16、React Native实战之TextInput组件
    14.React Native实战之Navigator组件初探
    13.React Native实战之Text组件
  • 原文地址:https://www.cnblogs.com/liuyangfirst/p/8763591.html
Copyright © 2020-2023  润新知