网络编程---NIO基本案例

欢迎大家访问我的个人博客:L_SKH’Blog

一、首先要知道NIO的三大组件以及他们之间的关系:

SelectorChannelBuffer 的关系图
在这里插入图片描述
关系图的说明:
1.每个channel 都会对应一个Buffer
2.Selector 对应一个线程, 一个线程对应多个channel(连接)
3.该图反应了有三个channel 注册到 该selector //程序
程序切换到哪个channel 是由事件决定的, Event 就是一个重要的概念
Selector 会根据不同的事件,在各个通道上切换
4.Buffer 就是一个内存块 , 底层是有一个数组
数据的读取写入是通过Buffer, 这个和BIO , BIO 中要么是输入流,或者是输出流, 不能双向,但是NIO的Buffer 是可以读也可以写, 需要 flip 方法切换
channel 是双向的, 可以返回底层操作系统的情况, 比如Linux , 底层的操作系统通道就是双向的.

二、基本流程

在这里插入图片描述
说明:
1.当客户端连接时,会通过ServerSocketChannel 得到 SocketChannel
2.Selector 进行监听 select 方法, 返回有事件发生的通道的个数.
3.将socketChannel注册到Selector上, register(Selector sel, int ops), 一个selector上可以注册多个SocketChannel
4.注册后返回一个 SelectionKey, 会和该Selector 关联(集合)
5.进一步得到各个 SelectionKey (有事件发生)。在通过 SelectionKey 反向获取 SocketChannel , 方法 channel()
6.最后可以通过 得到的 channel , 完成业务处理

三、代码实现

3.1NIOServer

package org.skh.nio;

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

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/26 17:19
 */

/**
 * 要注意这个buffer 客户端和channel之间存在
 * 服务器与通道之间也存在
 */
public class T07_NIOServer {
    public static void main(String[] args) throws IOException {

        //1.创建ServerSocketChannel 相当于ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //2.开启一个selector对象
        Selector selector = Selector.open();

        //3.绑定端口 在服务器监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

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

        //5.把serverChannel注册到Selector 关注accept事件 客户端连接
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //6.循环等待客户端连接
        while (true) {

            //让selector阻塞式的监听一秒看是否有事件发生
            if (selector.select(1000) == 0) {
                System.out.println("逝去的一秒内无客户端连接....");
                continue;
            }

            //!=0说明有事件发生 所以我们要取得selectionKey集合
            //得到关注事件的集合 进而得到对应的channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            //使用迭代器遍历
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while (keyIterator.hasNext()) { //更具相应的key的事件来进行处理
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    //连接事件 生成socketchannel 并注册到selector
                    //设置其事件为只读 并关联一个Buffer
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //将socketchannel设置为非阻塞模型
                    socketChannel.configureBlocking(false)  ;

                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                if (key.isReadable()) {

                    //通过key反向获取对应的channel
                    SocketChannel channel = (SocketChannel) key.channel();
                    //得到关联的buffer
                    ByteBuffer buffer = (ByteBuffer) key.attachment();

                    channel.read(buffer); //服务器与客户端之间也有buffer 所以要读取channel到buffer
                    System.out.println("From 客户端: " + new String(buffer.array(),0,buffer.position()));
                }
                //处理完之后就消除 避免重复处理
                keyIterator.remove();
            }


        }
    }
}

3.2NIOClient

package org.skh.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/26 18:49
 */
public class T08_NIOClient {
    public static void main(String[] args) throws IOException {
        //得到一个channel
        SocketChannel socketChannel = SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false) ;
        InetSocketAddress address = new InetSocketAddress("localhost", 6666);
        if (!socketChannel.connect(address)){
            //建立连接的过程需要时间 但因为是非阻塞的所以不会停留等待
            //可以去做其他事情 所以我们写一个while循环 以避免还没有连接就去发送数据
            //同时也可以很好的看到非阻塞的效果
            while (!socketChannel.finishConnect()){
                System.out.println("Doing Others While Connecting!!!");
            }
        }

        //已建立好连接 向客户端发送数据
        String msg =  "Rush_SKH" ;
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer)  ;
        System.in.read()  ;

    }
}

3.3Console:

逝去的一秒内无客户端连接....
逝去的一秒内无客户端连接....
From 客户端: Rush_SKH
逝去的一秒内无客户端连接....
逝去的一秒内无客户端连接....
逝去的一秒内无客户端连接....
逝去的一秒内无客户端连接....
发布了147 篇原创文章 · 获赞 62 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章