NIO 简单实例

前言

在 Java 中,数据传输 IO 模型大概分为三类:BIO(同步阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)。

在 BIO 中,服务器会针对每一个连接都去开一个新的线程进行处理,这样实现非常简单快速,但是对于资源消耗巨大,于是提出了 NIO。

在看本博客之前建议先了解一下 NIO 的基本用法。

一、NIO简介

NIO 是一种基于事件驱动的 IO 模型,面向缓冲区编程,NIO有三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。通俗理解,NIO 的一个线程管理一个SelectorSelector中管理多个客户端Channel,也就是说一个线程可以处理多个操作。

下面是三大组件的关系图:
在这里插入图片描述

二、聊天室实例

我们通过 NIO ,来实现一个简单的群聊系统,进而学习 NIO 在服务端与客户端中所用到的API。

1.服务端介绍

①首先我们在服务端定义了 SelectorServerSocketChannel,以及当前服务器暴露的端口号;
在这里插入图片描述
②初始化这些组件信息;
在这里插入图片描述

  1. 打开选择器(Channel都会由Selector统一管理);
  2. 打开服务端 Socket 通道,并绑定监听的端口号(类似于 BIO 中的 ServerSocket);
  3. serverSocketChannel.configureBlocking(false);设置当前通道为非阻塞状态;
  4. 将服务器 Socket 通道注册进Selector,由选择器统一管理。

③服务端监听并处理客户端事件

需要清楚下面两个方法的作用。

selector.select():返回已经准备就绪的通道个数(这些通道包含你感兴趣的的事件)。
	比如:你对读就绪的通道感兴趣,那么select()方法就会返回读事件已经就绪的那些通道。
selector.selectedKeys():获取就绪通道的事件列表。

SelectionKey中封装了事件的四种类型:

  • OP_READ:可读事件;值为:1<<0
  • OP_WRITE:可写事件;值为:1<<2
  • OP_CONNECT:客户端连接服务端的事件(tcp连接),一般为创建SocketChannel客户端channel;值为:1<<3
  • OP_ACCEPT:服务端接收客户端连接的事件,一般为创建ServerSocketChannel服务端channel;值为:1<<4

服务端不断监听是否有事件发生,根据不同的事件类型做不同的处理。

public void listen() {
   
        try {
   
            while (true) {
   
                int count = selector.select(2000);
                if (count > 0) {
   
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();

                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
   
                        SelectionKey selectionKey = iterator.next();
                        if (selectionKey.isAcceptable()) {
   
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + " 上线...");
                        }
                        if (selectionKey.isReadable()) {
   
                            readClientData(selectionKey);
                        }

                        iterator.remove();
                    }
                }
            }
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

在处理完这个事件之后一定要进行移除,防止多线程重复操作。

服务器 读取客户端数据、转发客户端数据 方法可参考后面总代码。

2.客户端介绍

①通过ip和端口连接服务器端并且注册到 Selector
在这里插入图片描述
②开启一个线程不断的获取事件进行处理
在这里插入图片描述
这样就算完成了服务端和用户端的实现,下面是所有的代码:

服务端 →

package com.kiger.nio.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值