美文网首页
NIO 单线程完成的在线字符数统计用例

NIO 单线程完成的在线字符数统计用例

作者: JohnYuCN | 来源:发表于2022-06-11 22:42 被阅读0次
package cn.johnyu;

import java.net.InetSocketAddress;
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.Arrays;
import java.util.Iterator;

public class MyApp {
    public static void main(String[] args) throws Exception{
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        Selector selector = Selector.open();
        int userNum=0;
        /* 打开ServerSocketChannel,并以名称为 ACCEPT 的事件向Selector注册 */
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        /* 监听6000端口,但主线程并不阻塞在此处 */
        serverSocketChannel.bind(new InetSocketAddress(6000));
        System.out.println("服务器启动,监听 6000 端口...");
        while (true){
            /* 此处是程序的关键,如没有任何注册事件发生,主线程将在这阻塞 */
            selector.select();
            /*一旦有注册的事件的发生,主线程将在此向下执行*/
            /* 执行的逻辑:
            遍历事件队列,根据事件队列的名称及相关条件进行事件的回调处理;
            如果是 ACCEPT ,则获取相应的 SocketChannel,并向Selector 进行注册(READ事件);
            再向用户输出提示,如果用户输入是 bye 则表示输入结束 */
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isAcceptable()){
                    ++userNum;
                    ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    //ScokcetChannel 以 事件 READ 注册,同是增加一个附加对象,用于绑定会话级的信息,本例表示总计数器
                    socketChannel.register(selector,SelectionKey.OP_READ,new Counter(0));
                    buffer.put(("您是系统的第 "+userNum+" 个用户,请输入信息,我将为您统计字数(输入 bye 退出!):\n").getBytes("utf8"));
                    buffer.flip();
                    socketChannel.write(buffer);
                    buffer.clear();
                    System.out.println("有客户连接成功,当前用户数:"+userNum);
                }
                if(selectionKey.isReadable()){
                    SocketChannel channel= (SocketChannel)selectionKey.channel();
                    //获取用户输入的信息和输入的字符数,如果字符数等于0时,表示异常退出,大于0表示正常输入
                    int len=channel.read(buffer);
                    if(len>0){
                        //从Buffer中copy数组并转换成为数组
                        String userMessage=new String(Arrays.copyOfRange(buffer.array(),0,len),"utf8");
                        //用户输入 bye 时的处理:关闭Channel和连接,并减少用户数量
                        if(userMessage.trim().equals("bye")){
                            channel.close();
                            selectionKey.cancel();
                            userNum--;
                            System.out.println("有用户正常退出,当前用户数是:"+userNum +"个");
                        }
                        else{
                            Counter totalCounter=(Counter)selectionKey.attachment();//获取注册时绑定的会话级对象
                            totalCounter.incr(userMessage.length()-1);//累计字符数
                            String resp="您输入的字符个数:"+(userMessage.length()-1)+",您输入的总字符个数是:"+totalCounter.getCount()+",总用户数:"+userNum+"\n";
                            /*以下四行代码逻辑:
                            向空的Buffer中写入信息,复位position=0,limit=字节数,向Channel中写入,清空Buffer*/
                            buffer.put(resp.getBytes("utf8"));
                            buffer.flip();
                            channel.write(buffer);
                            buffer.clear();
                        }
                    }
                    else{//用户强制断网时
                        channel.close();
                        selectionKey.cancel();
                        userNum--;
                        System.out.println("异常退出,当前用户数是:"+userNum +"个");
                    }

                }
                iterator.remove();
            }
        }
    }
}

class Counter{
    public Counter(int count) {
        this.count = count;
    }

    private int count;

    public int getCount() {
        return count;
    }

    public void incr(int step) {
        this.count+=step;
    }
}

相关文章

网友评论

      本文标题:NIO 单线程完成的在线字符数统计用例

      本文链接:https://www.haomeiwen.com/subject/kewwmrtx.html