Java NIO 代码示例

NIOServer

package com.ye.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.Channel;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @author yeWanQing
 * @since 2019/3/27
 */
public class NIOServer {
    //selector
    private static  Selector selector;
    //字符编码
    private Charset charset = Charset.forName("UTF-8");
    //内容协议
    private static String USER_CONTENT_SPLIT = "#@#";
    //用户存在的提示
    private static String USER_EXIST = "系统提示:该昵称已经存在,请换一个昵称";
    //用来记录在线人数,以及昵称
    private static Set<String> users = new HashSet<>();

    public NIOServer(InetSocketAddress address){
        try {
            //获取selector
            selector = Selector.open();
            //获取ServerSocketChannel
            ServerSocketChannel chanel = ServerSocketChannel.open();
            //绑定地址和端口
            chanel.bind(address);
            //设置为非阻塞的,jdk为了兼容,默认为阻塞的
            chanel.configureBlocking(false);
            //将ServerSocketChannel注册到selector上,感兴趣的事件为接受连接
            chanel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void listen(){
        try {
            while (true){
                //获取事件,这一步是阻塞的,所以用while(true)没有关系,返回的是基于上一次select之后的事件
                int select = selector.select();
                if(select == 0){
                    continue;
                }
                //返回就绪事件列表 SelectionKey包含事件信息
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //处理事件业务
                    processKey(selectionKey);
                    //移除事件 因为selector不会自动移除,如果不收到移除,下次selectedKeys()还会继续存在该selectionKey
                    iterator.remove();
                }
            }
        }catch (Exception e){

        }

    }

    private void processKey(SelectionKey selectionKey) throws IOException {
        if(selectionKey.isAcceptable()){//事件类型为接受连接
            //获取对应的ServerSocketChannel
            ServerSocketChannel server = (ServerSocketChannel)selectionKey.channel();
            //为每一个连接创建一个SocketChannel,这个SocketChannel用来读写数据
            SocketChannel client = server.accept();
            //设置为非阻塞
            client.configureBlocking(false);
            //注册selector 感兴趣事件为读数据,意思就是客户端发送写数据时,selector就可以接收并读取数据
            client.register(selector, SelectionKey.OP_READ);
            //继续可以接收连接事件
            selectionKey.interestOps(SelectionKey.OP_ACCEPT);
        }else if(selectionKey.isReadable()){//事件类型为读取数据
            //得到SocketChannel
            SocketChannel client = (SocketChannel)selectionKey.channel();
            //定义缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            StringBuilder content = new StringBuilder();
            while (client.read(buffer) > 0){
                //buffer由写模式变成读模式,因为client.read(buffer)是从管道写数据到缓冲区中
                buffer.flip();
                content.append(charset.decode(buffer));
            }
            //清空缓冲区
            buffer.clear();
            //继续注册读事件类型
            selectionKey.interestOps(SelectionKey.OP_READ);
            //业务处理
            if(content.length() > 0){
                String[] arrayContent = content.toString().split(USER_CONTENT_SPLIT);
                if(arrayContent != null && arrayContent.length == 1) {
                    String nickName = arrayContent[0];
                    if(users.contains(nickName)) {
                        client.write(charset.encode(USER_EXIST));
                    } else {
                        users.add(nickName);
                        int onlineCount = onlineCount();
                        String message = "欢迎 " + nickName + " 进入聊天室! 当前在线人数:" + onlineCount;
                        broadCast(null, message);
                    }
                }
                else if(arrayContent != null && arrayContent.length > 1) {
                    String nickName = arrayContent[0];
                    String message = arrayContent[1];
                    message = nickName + " 说 " + message;
                    if(users.contains(nickName)) {
                        //不回发给发送此内容的客户端
                        broadCast(client, message);
                    }
                }

            }
        }
    }

    public void broadCast(SocketChannel client, String content) throws IOException {
        //广播数据到所有的SocketChannel中
        for(SelectionKey key : selector.keys()) {
            java.nio.channels.Channel targetChannel = key.channel();
            //如果client不为空,不回发给发送此内容的客户端
            if(targetChannel instanceof SocketChannel && targetChannel != client) {
                SocketChannel target = (SocketChannel)targetChannel;
                target.write(charset.encode(content));
            }
        }
    }

    public int onlineCount() {
        int res = 0;
        for(SelectionKey key : selector.keys()){
            Channel target = key.channel();
            if(target instanceof SocketChannel){
                res++;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8081);
        new NIOServer(address).listen();
    }
}
复制代码

NIOClient

package com.ye.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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * @author yeWanQing
 * @since 2019/3/27
 */
public class NIOClient {
    private Selector selector = null;
    private SocketChannel socketChannel = null;
    private String nickName = "";
    private Charset charset = Charset.forName("UTF-8");
    private static String USER_EXIST = "系统提示:该昵称已经存在,请换一个昵称";
    private static String USER_CONTENT_SPLIT = "#@#";
    public NIOClient(InetSocketAddress serverAddress){
        try {
            //获取selector
            selector = Selector.open();
            //获取socketChannel
            socketChannel = SocketChannel.open();
            //连接到服务
            socketChannel.connect(serverAddress);
            //设置为非阻塞
            socketChannel.configureBlocking(false);
            //向selector注册感兴趣事件 读数据类型
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void session(){
        //开辟一个新线程从服务器端读数据
        new Reader().start();
        //开辟一个新线程往服务器端写数据
        new Writer().start();
    }

    private class Reader extends Thread {
        public void run() {
            try {
                //轮询
                while(true) {
                    int readyChannels = selector.select();
                    if(readyChannels == 0) continue;
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();  //可以通过这个方法,知道可用通道的集合
                    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                    while(keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();
                        keyIterator.remove();
                        process(key);
                    }
                }
            }
            catch (IOException io){

            }
        }

        private void process(SelectionKey key) throws IOException {
            if(key.isReadable()){
                SocketChannel sc = (SocketChannel)key.channel();
                ByteBuffer buff = ByteBuffer.allocate(1024);
                String content = "";
                while(sc.read(buff) > 0)
                {
                    buff.flip();
                    content += charset.decode(buff);
                }
                //若系统发送通知名字已经存在,则需要换个昵称
                if(USER_EXIST.equals(content)) {
                    nickName = "";
                }
                System.out.println(content);
                key.interestOps(SelectionKey.OP_READ);
            }
        }
    }

    private class Writer extends Thread{

        @Override
        public void run() {
            try{
                //在主线程中 从键盘读取数据输入到服务器端
                Scanner scan = new Scanner(System.in);
                while(scan.hasNextLine()){
                    String line = scan.nextLine();
                    if("".equals(line)) continue; //不允许发空消息
                    if("".equals(nickName)) {
                        nickName = line;
                        line = nickName + USER_CONTENT_SPLIT;
                    } else {
                        line = nickName + USER_CONTENT_SPLIT + line;
                    }
                    socketChannel.write(charset.encode(line));//client既能写也能读,这边是写
                }
                scan.close();
            }catch(Exception e){

            }
        }
    }

    public static void main(String[] args) {
        InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 8081);
        new NIOClient(serverAddress).session();
    }

}
复制代码

转载于:https://juejin.im/post/5c9b7ac9f265da611e178644

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值