栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

网络编程之中篇——重中之重NIO模型开篇

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

网络编程之中篇——重中之重NIO模型开篇

1、NIO的介绍 1.1、NIO概念

         NIO(New IO)同步非阻塞IO模型,采用了事件驱动的思想来实现一个复用器,来解决大并发的问题

NIO中,将读、写、可连接、可接受等操作在BIO中是阻塞处理,将操作设置为非阻塞,并将这些操作视为事件,当关注某个事件时,将其注册到复用器Selector(本质上使用底层操作系统提供的IO复用器:select、poll,epoll)。由系统来监听事件是否准备就绪, 当socket有读或者可写操作时,系统会通知相应的用户程序来处理,将流读取到缓冲区或者写入到系统中

一个复用器上是可以注册多个用户的连接,有效的用户请求才会来通知用户程序,用户程序才进行介入处理,

1.2、NIO处理过程

1.3、几个概念的介绍

1.3.1、复用器(Selector) 也叫做多路选择器,作用是检查一个或者多个NIO的channel(Channel),可以实现单线程管理多个channel,也可以管理多个网络请求

1.3.2、channel:

通道,用户IO操作的连接,对原有的IO补充,不能直接访问数据需要和缓冲区BUffer结合使用,通道主要有

1.3.3、SocketChannel:

主要是用户连接服务端,一般在客户端实现

1.3.4、ServerSocketChannel:

监听新进来的TCP连接,对于每一个新用户连接创建一个SocketChannel实例,一般在服务端实现

1.3.5、Buffer:缓冲区

IO流中的数据需要经过缓冲区交给channel

2、NIO编码

2.1、服务端编程流程:

2.1.1、实例ServerSocketChannel实例【ServerSocketChannel.open()】

2.1.2、服务端绑定端口【bind】

2.1.3、将serversocketchannel设置为非阻塞【serverSocketChannel.configureBlocking(false)】

2.1.4、实例话selector实例【Selector.open()】

2.1.5、将serversocketchannel注册到selector上,并关注可接受事件【serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT)】

2.1.6、selector阻塞等待至少一个事件发生【selector.select()】

2.1.7、有感兴趣事件发生,遍历感兴趣事件集合

2.1.8、如果是可接受事件、通过serversocketchannel调用accept获取socketchannel

2.1.9、将socketchannel设置为非阻塞

2.1.10、将socketchannel注册到selector复用上,并关注可读、可写事件

2.1.11、循环等待感兴趣事件集合,即跳转至第6步

2.1.12、如果当前是可读事件、通过socketchannel调用read来进行读数据操作

2.1.13、关闭资源

服务端代码:
public class Server {
    public static void main(String[] args) {
        ServerSocketChannel serverSocketChannel = null;
        try {
            //创建ServerSocketchannel实例
            serverSocketChannel = ServerSocketChannel.open();

            //绑定端口
            serverSocketChannel.bind(new InetSocketAddress(7777));
            System.out.println("服务端启动啦");

            //设置ServerSocketchannel为非阻塞  true:表示则塞 false:非阻塞
            serverSocketChannel.configureBlocking(false);


            //创建复用器selector实例
            Selector selector = Selector.open();

            //将serverSocketChannel注册到selector中,并关注的是可接受事件
            
            serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT | SelectionKey.OP_WRITE);


            //selector阻塞等待事件发生
            while (selector.select() >= 0) {
                //获取已发生事件集合
                Iterator  iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    //删除已完成事件
                    iterator.remove();
                    if (selectionKey.isValid() && selectionKey.isAcceptable()) {
                        System.out.println("有接收事件发生");
                        //有可接受事件发生
                        //SelectableChannel 所有通道的父类,ServerSocketChannel、SocketChannel
                        ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel();
                        //通过调用accept获取SocketChannel实例
                        SocketChannel socketChannel = serverSocketChannel1.accept();
                        System.out.println("有新用户连接啦"+socketChannel.getRemoteAddress());

                        //设置为非阻塞
                        socketChannel.configureBlocking(false);

                        //将socketChannel注册到selector复用器上,并关注读事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }

                    if (selectionKey.isValid() && selectionKey.isReadable()) {
                        System.out.println("有可读事件发生");
                        //有可读事件发生
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                        //读取数据
                        //创建一个缓存实例
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        //将数据从channel写入缓存
                        int read = socketChannel.read(buffer);
                        //读写模式切换
                        buffer.flip();

                        //创建有效大小的byte数组
                        byte[] bytes = new byte[read];
                        buffer.get(bytes);

                        String msg = new String(bytes);
                        System.out.println("数据为:"+msg);

                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            System.out.println("服务端关闭啦");
            if (serverSocketChannel != null) {
                try {
                    serverSocketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.2、客户端编码流程:

2.2.1、创建socketchannel实例

2.2.2、将socketchannel设置非阻塞

2.2.3、创建Selector复用器实例

2.2.4、将socketchannel实例注册到Selector实例中,并关注可连接事件

2.2.5、socketchannel主动调用connect连接服务端,会立即返回结果

2.2.6、如果结果为false:当前连接还未完成,通过selector.select由系统监听事件发生,

2.2.7、进行读写事件

2.2.8、关闭资源

客户端代码:

public class Client {
    public static void main(String[] args) {
        SocketChannel socketChannel = null;
        try {
            //创建socketChannel实例
            socketChannel = SocketChannel.open();

            //将socketChannel设置为非阻塞
            socketChannel.configureBlocking(false);

            //创建Selector复用器实例
            Selector selector = Selector.open();

            //将socketChannel注册到Selector实例中,并关注可连接事件
            socketChannel.register(selector,SelectionKey.OP_CONNECT);

            //主动连接服务端,该方法不会阻塞,会立即返回结果,要么成功,要么未连接成功,则交由os等待连接过程
            if (!socketChannel.connect(new InetSocketAddress("127.0.0.1", 7777))) {
                //返回为false,表示连接还未连接成功
                System.out.println("connect还未完成!");

                //等待系统返回就绪事件
                selector.select();

                //进行完成连接操作
                socketChannel.finishConnect();
            }
            System.out.println("客户端连接服务端成功");


            //进行读写事件
            byte[] bytes = "Hello Tulun".getBytes();
            //创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(bytes.length);

            //往Buffer写数据
            buffer.put(bytes);

            //读写模式切换
            buffer.flip();

            //往socketchannel通道中写数据
            socketChannel.write(buffer);



        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if (socketChannel != null) {
                System.out.println("客户端关闭啦");
                try {
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/310447.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号