关于NIO的一些记录

netty和MIna他们是同一个架构开发的,封装了很多JDK的NIO.

1.我们为什么不直接使用jdk NIO?

API繁杂,使用非常麻烦.原始NIO可靠性比较低下,JDK的NIO存在epoll这个BUG,会导致Selector空轮询,官方说jdk1.6的update18版本已经修复,但是很多企业实践测试 还是存在这样的问题 ,只是触发概率降低.

2.netty可以运用在那些领域?

1分布式进程通信
例如: hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x

2、游戏服务器开发
最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x
案例:游戏玩家上线后的信息 缓存 下线后的清除
SimpleChannelHandler 处理消息接收和写
{
messageReceived接收消息
channelConnected新连接,通常用来检测IP是否是黑名单
channelDisconnected链接关闭,可以再用户断线的时候清楚用户的缓存数据等
}

Java1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能的I/O程序的时候,会面临着巨大的挑战和困难,主要问题如下:
1.没有数据缓冲区,I/O性能存在问题。NIO 结构增加数据缓冲区
2.没有C或者C++中的Channel(双高速管道 写入 读出 写入读出同时存在的业务)概念,只有输入和输出流。传统的IO管道 改善成Channel
3.线程模型方面:同步阻塞式I/O通信(BIO),通常会导致通信线程被长时间阻塞;
在Java至此异步I/O之前的很长一段时间里,高性能服务端的开发领域一直是被C++和C长期占据.
NIO:同步非阻塞

4.增加多路复用机制 Selector

阻塞IO模型:就好像去食堂排队打饭,只要前面的没完成,就会被阻塞,效率底下

非阻塞IO模型:IO任务执行到这点 ,如果这个点的数据或者其他的组件没有准备好的话,这个任务它不停留在这里,而是去执行其他的任务。

NIO编程,非阻塞式IO通信模型,采用Epoll多路复用机制
1.缓冲区buffer(增大IO吞吐性能)
2.高速通道Channel(业务包容性增大 减少频繁创建其他管道)
与流不同的是 双向
3.多路复用器Selector,用来不断的轮询注册到上面的多个Channel
SelectionKey 可以获取这些就绪通道的集合
Epoll()取代了传统的select实现,没有最大句柄的1024/2048的限制,
只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端接入
4.NIO专用的服务类:ServerSocketChannel
ServerSocket io
ServerSocketChannel 服务类配合我们高速通道Channel

下面是一个demo

package wtz.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Created by wtz on 2018/9/4.
 */
public class NIOServer {

    private static ServerSocketChannel serverSocketChannel;

    private static int port = 8080;

    //信息写入
    ByteBuffer rcBuffer = ByteBuffer.allocate(1024);

    //信息写出
    ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

    //多路复用器
    private static Selector selector;

    //维护一个实际上是一个事件标签的集合
    Map<SelectionKey, String> sessionMsg = new HashMap<SelectionKey, String>();

    //模仿Netty开辟线程池 jdk 四种类型


    public NIOServer(int port) throws IOException {
        this.port = port;
        //打开通道
        serverSocketChannel = ServerSocketChannel.open();
        //绑定端口地址
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        //设置不阻塞
        serverSocketChannel.configureBlocking(false);
        //打开多路复用器
        selector = Selector.open();
        //通道注册多路复用器让其为通道工作
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("NIO服务已经启动,且监听端口是" + this.port);
    }

    //监听请求的方法模块
    public void listener() throws IOException {
        while (true) {
            //进来就是通过selector来看有没有注册事件
            int eventCount = selector.select();
            if (eventCount <= 0) {
                //继续轮询 NIO内部机制:不断的轮询注册到上面的多个Channel
                continue;
            }
            //获取到就绪通道的集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //处理客户端的事件
                process(iterator.next());
                //移除集合里面的事件
                iterator.remove();
            }
        }
    }

    //详细的处理客户端事件
    private void process(SelectionKey selectionKey) {
        SocketChannel client = null;
        try {
            if (selectionKey.isValid() && selectionKey.isAcceptable()) {
                client = serverSocketChannel.accept();
                client.configureBlocking(false);
                //告诉我们的复用器,下次可以读数据
                client.register(selector, SelectionKey.OP_READ);
            } else if (selectionKey.isValid() && selectionKey.isReadable()) {
                //读到缓冲区
                rcBuffer.clear();
                client = (SocketChannel) selectionKey.channel();
                int len = client.read(rcBuffer);
                if (len > 0) {
                    System.out.println("服务端收到客户端的消息:" + new String(rcBuffer.array(), 0, len));
                    String msg = new String(rcBuffer.array(), 0, len);
                    sessionMsg.put(selectionKey, msg);
                    //告诉我们的复用器,下次可以写数据
                    client.register(selector, SelectionKey.OP_WRITE);
                }
            } else if (selectionKey.isValid() && selectionKey.isWritable()) {
                if (!sessionMsg.containsKey(selectionKey)) {
                    return;
                }
                //服务器回客户端消息
                client = (SocketChannel) selectionKey.channel();
                sendBuffer.clear();
                sendBuffer.put((new String(sessionMsg.get(selectionKey)) + ",你好,你的请求wtz已经处理").getBytes());
                //这个实际上是设置读取位
                sendBuffer.flip();
                client.write(sendBuffer);
                //告诉我们的复用器,下次可以读数据
                client.register(selector, SelectionKey.OP_READ);
            }
        } catch (IOException e) {
            //读取key的事件的时候,遇到客户端异常下线,不会引起异常
            try {
                selectionKey.cancel();
                client.socket().close();
                client.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    //简单的NIO消息通讯服务器
    public static void main(String[] args) {
        try {
            new NIOServer(port).listener();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
package wtz.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * Created by wtz on 2018/9/4.
 */
public class NIOClient {
    SocketChannel client;

    InetSocketAddress serverAddress = new InetSocketAddress("localhost", 8080);

    Selector selector;

    ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
    ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

    public NIOClient() throws IOException {
        client = SocketChannel.open();
        client.configureBlocking(false);
        client.connect(serverAddress);
        selector = Selector.open();
        client.register(selector, SelectionKey.OP_CONNECT);
    }

    public void session() throws IOException {
        if (client.isConnectionPending()) {
            client.finishConnect();
            client.register(selector, SelectionKey.OP_WRITE);
            System.out.println("已经连接到服务器,可以在控制台登记了");
        }

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            //键盘输入的内容
            String name = scanner.nextLine();
            if ("".equals((name))) {
                continue;
            }
            if ("finish".equals(name)) {
                System.exit(0);
            }

            process(name);
        }
    }

    private void process(String name) throws IOException {
        boolean waitHelp = true;
        Iterator<SelectionKey> iterator = null;
        Set<SelectionKey> keys = null;
        while (waitHelp) {
            try {
                int readys = selector.select();
                //如果没有客人,继续轮询
                if (readys == 0) {
                    continue;
                }

                keys = selector.selectedKeys();

                iterator = keys.iterator();

                //一个一个key迭代检查
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();

                    if (key.isValid() && key.isWritable()) {
                        //首先是判断是否可写,可写,就是代表客户端要对服务端发送信息
                        sendBuffer.clear();
                        sendBuffer.put(name.getBytes());
                        sendBuffer.flip();

                        client.write(sendBuffer);

                        client.register(selector, SelectionKey.OP_READ);
                    } else if (key.isValid() && key.isReadable()) {
                        //服务端发送信息回来给客户端,去读
                        receiveBuffer.clear();
                        int len = client.read(receiveBuffer);
                        if (len > 0) {
                            receiveBuffer.flip();
                            System.out.println("服务端反馈的消息:" + new String(receiveBuffer.array(), 0, len));
                            client.register(selector, SelectionKey.OP_WRITE);
                            waitHelp = false;
                        }
                    }

                    //检查完之后,打发客户走
                    iterator.remove();
                }
            } catch (IOException e) {
                ((SelectionKey) keys).cancel();
                client.socket().close();
                client.close();
                return;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new NIOClient().session();
    }
}

这里写图片描述
这里写图片描述
这里写图片描述
以上就是关于NIO的一些记录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值