NIO学习心得

这几天学习NIO…难受了好几天,今天20点30终于明白的七七八八,记下来。。
NIO和前几天的TCP聊天室有点像,对照来看,会舒服点,所以也写一个聊天室。
Selector 选择器
什么选择器就是个“集合”-----类似于map的集合,可以用来记录每一台客户端和服务器的通讯。
为啥说他类似于map呢?因为他也类似<key ,value>只不过map的key和value都是数据,Selecter的key是selectionKey,value是“事件”;都是可以通过“key”去操作“value”;

  • 选择器是单例只能实例一个,所以通过open()来实例化

ServerSocketChannel
SernerSocketChannel —我叫他大管道。在一个NIO服务器里只可以有一个。负责开辟端口,设置这个NIO是不是非阻塞模式的。理解的时候可以类比 网络编程的ServerSocket。(有点类似)

  • ServerSocketChannel 也是单例

SocketChannel
*这个东西 —我叫他小管道,可以插到大管道上。(可以插好多个的那个,因为可以插好多所以就可以省去多线程)NIO中SocketChannel通过与客户端通讯。一个客户端一个小管道。SocketChannel用来传输数据。理解他可以与网络编程的Socket对比。
SelectionKey选择键(selectionKey)
选择键就是选择器的“key” 在注册的时候,作为“小管道”与事件的标记,来标记这个事件是哪个小管道的。
注册
注册就是把你要做的事情放到选择器里面 这个你要做的事情就会去选择器排队,等着执行
选择器结构

小管道要执行的操作SelectionKey
SocketChannl操作相当于这个的id

操作只有三种 建立连接 读文件 写文件
每次注册一个。就会多一个这个结构

小管道要执行的操作SelectionKey
socketchannl1readkey1
socketchannl2readkey2
socketchannl1writekey3
socketchannl2writekey4
socketchannl1readkey5
socketchannl2readkey6

代码 服务器

public class NIOServerP {
    static Map<SocketChannel, String> onlinopeople = new HashMap<SocketChannel, String>();
//这个Map集合用来记录有几个用户登录(记录有几个小管道)
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();//选择器是单例只能实例一个,所以通过open()来实例化
        ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();//大管道也是一样的,单例
        serverSocketChannel.configureBlocking(false);//设置非阻塞模式
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//大管道注册
        SocketAddress address = new InetSocketAddress(18888);
        serverSocketChannel.socket().bind(address);//给管道绑定端口
        while (true){/死循环 一直监控没有可以执行的事件
            if (selector.select(3000) == 0){//3秒监控一次
                if(onlinopeople.size() == 0){
                    System.out.println("还没有人上线");
                    continue;
                }else {
                    System.out.println("上线人数为"+onlinopeople.size());
                    continue;
                }
            }
            System.out.println("有人上线");
            boolean stute = keyhander(selector);
            if(stute){
                break;
            }
        }
    }
    public  static boolean  keyhander(Selector selector) throws IOException {
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//将选择器放入迭代器
        while (iterator.hasNext()){                                 //判断每一个事件是什么操作 连接?读?写?②
            SelectionKey key = iterator.next();
            if (key.isAcceptable()) {                               // 连接?②
                System.out.println("上线人数为"+(onlinopeople.size()+1));
                Buffer buffer = ByteBuffer.allocate(1024);
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                onlinopeople.put(socketChannel,null);
                socketChannel.register(key.selector(),SelectionKey.OP_READ,buffer);//带着收到的缓存区的数据去注册①
            }else if(key.isReadable()){                              //读?②
                System.out.println("有新数据");
                StringBuffer sb= new StringBuffer();
                SocketChannel socketChannel = (SocketChannel) key.channel();//通过key操作管道。把管道“接”到这变量里
                ByteBuffer byteBuffer = (ByteBuffer) key.attachment();   //拿到注册时一起带着的数据   ①
                while (socketChannel.read(byteBuffer) > 0){      //这个地方可以类比inputstream
                    byteBuffer.flip();                                      //byteButter 缓存类的操作。。。不懂百度③
                    while (byteBuffer.hasRemaining()){
                        char[] arrayChar = Charset.forName("UTF-8").decode(byteBuffer).array();
                        sb.append(arrayChar);
                    }
                    byteBuffer.clear();                                  //③
                }
                System.out.println("收到的数据为"+sb);
                //收到信息后向客户端返回消息
                byteBuffer.clear();                                   //③
                String  message = "我收到";
                byteBuffer.put(message.getBytes());
                socketChannel.register(key.selector(),SelectionKey.OP_WRITE,byteBuffer);//带着收到的缓存区的数据去注册
            }else if(key.isWritable()){                                 //写②
                System.out.println("开始输出数据");
                SocketChannel socketChannel =(SocketChannel) key.channel();
                ByteBuffer byteBuffer = (ByteBuffer)key.attachment();//
                for(Map.Entry<SocketChannel,String> entry : onlinopeople.entrySet()){     //通过map给每个登录的客户端发送消息
                    byteBuffer.rewind();                    //③
                    entry.getKey().write(byteBuffer);
                }
                socketChannel.register(key.selector(),SelectionKey.OP_READ,byteBuffer);
                byteBuffer.clear();                        //③
            }else   if(key.isValid()){
                System.out.println("有异常无效事件。【可尝试重新处理该事件】");
            } else {
                System.out.println("有其它情况");
            }
            iterator.remove();                  //操作完成后删掉注册的东西
        }
        return false;
    }
}

代码 客户端

public class NIOClient {
    public static void main(String[] args) throws IOException {
        //选择器 初始化(实例化+数据初始化)、实例化==创建一个类对象
        Selector selector = Selector.open();
        //初始化套接字连接通道。
        SocketChannel channel = SocketChannel.open();
        //NIO:支持两种阻塞式、非阻塞式
        channel.configureBlocking(false);
    //在该服务通道上绑定IP、端口
    SocketAddress ip = new InetSocketAddress("127.0.0.1", 18888);
    channel.connect(ip);

    //向selector注册感兴趣的Key事件
    channel.register(selector, SelectionKey.OP_CONNECT);
    while (true) {
        selector.select();
        Set<SelectionKey> keys = selector.selectedKeys();
        handleKeys(keys);
    }
}

public static void handleKeys(Set<SelectionKey> keys) throws IOException {
    Iterator<SelectionKey> iterator = keys.iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isConnectable()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);  //==> byte[1024]
            //建立连接
            SocketChannel channel = (SocketChannel) key.channel();
            //与服务器建立稳定连接
            channel.finishConnect();
            System.out.println("与服务建立稳定连接!");

            channel.register(key.selector(), SelectionKey.OP_WRITE, buffer);
        } else if (key.isWritable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            System.out.println("客户端将向服务器发送数据,请输入:");
            //向客户端任意输出点东西
            Scanner scanner = new Scanner(System.in);
            String message = scanner.next();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.put(message.getBytes());
            buffer.flip();
            channel.write(buffer);
            buffer.clear();
            channel.register(key.selector(), SelectionKey.OP_READ, buffer);
        } else if (key.isReadable()) {
            System.out.println("接收客户端数据");
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            //读取OS已经帮你接收完毕的数据
            StringBuffer sb = new StringBuffer();
            while (channel.read(buffer) > 0) {
                buffer.flip();
                while (buffer.hasRemaining()) {
                    char[] arrayChar = Charset.forName("UTF-8").decode(buffer).array();
                    String temp = new String(arrayChar);
                    temp = temp.trim();
                    sb.append(temp);
                }
                buffer.clear();
            }
            System.out.println("客户端接收到的服务器数据为:" + sb);

            buffer.clear();
            channel.register(key.selector(), SelectionKey.OP_WRITE, buffer);

        } else if (key.isValid()) {
            System.out.println("通讯异常,是否重试处理或限定次数。");
            //continue;
        }
        iterator.remove();
    }
}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值