NIO--SocketChanel和ServerSocketChannel实现简易群聊改造--单Reactor多线程模式

26 篇文章 1 订阅
13 篇文章 0 订阅

架构图:


 

  • Reactor

import java.net.InetSocketAddress;
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.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NIOReactorServer {
    //服务所用端口
    private static final int PORT=9999;
    //选择器
    private Selector selector;
    //服务端serverSocketChannel, 主要功能是处理事件
    private ServerSocketChannel serverSocketChannel;

    //处理连接的acceptor
    private Acceptor acceptor;

    //线程池
    private ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

    //初始化服务端
    {
        try {
            //创建选择器
            selector=Selector.open();
            //创建serverSocketChannel
            serverSocketChannel=ServerSocketChannel.open();
            //设置为非阻塞式
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //把serverSocketChannel注册到选择器,使选择器监听accept事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public Acceptor getAcceptor() {
        return acceptor;
    }

    public void setAcceptor(Acceptor acceptor) {
        this.acceptor = acceptor;
    }

    public void dispatch(){
        //当有事件发生时,获取发生事件的SelectionKey集合,通过SelectionKey可以获取socketChannel通道和发生事件的类型等
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            System.out.print(Thread.currentThread().getName()+"监听到事件:");
            SelectionKey selectionKey = iterator.next();
            //accept事件处理
            if (selectionKey.isAcceptable()) {
                System.out.println(Thread.currentThread().getName()+"连接事件");
                if(acceptor!=null)
                    acceptor.accept(selector,serverSocketChannel);
            }
            //read事件处理
            if (selectionKey.isReadable()) {
                System.out.println(Thread.currentThread().getName()+"消息读取事件");

                //群发消息(不包含自己)
                MsgHandler msgHandler=MsgHandlerPool.getMsgHandler();
                msgHandler.setSelector(selector);
                msgHandler.setSelf(selectionKey);

                //读消息
                String msg=msgHandler.read(selectionKey);
                System.out.println(Thread.currentThread().getName()+"服务器群发消息:" + msg);

                //处理业务 发送消息
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                String userName ="未知";
                try {
                    userName = socketChannel.getRemoteAddress().toString().substring(11);
                }catch (Exception e){
                    e.printStackTrace();
                }

                //调用msgHandler的work方法做后续工作,主线程可以返回了
                msgHandler.work(userName,msg);

                System.out.println(Thread.currentThread().getName()+"主线程返回");
            }
            //移出处理完事件的selectionKey
            iterator.remove();
        }
    }
    //事件监听:accept和read事件
    public void select(){
        while (true) {
            try {
                System.out.println(Thread.currentThread().getName()+"开始监听事件");
                //当没有事件发生时会阻塞在此位置,也可设置不阻塞或阻塞超时时间
                selector.select();

                //事件分发
                dispatch();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        //线程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        //创建服务端
        NIOReactorServer server=new NIOReactorServer();
        //指定连接处理器
        server.setAcceptor(new Acceptor());
        //开始监听
        server.select();
    }
}
  • Acceptor(未变动)

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * 处理连接对象
 */
public class Acceptor {
    public void accept(Selector selector, ServerSocketChannel serverSocketChannel) {
        try {
            System.out.println(Thread.currentThread().getName()+"分配一个代表客户端连接的socketChannel");
            //使用serverSocketChannel分配一个代表客户端连接的socketChannel
            SocketChannel socketChannel = serverSocketChannel.accept();
            //设置为非阻塞式
            socketChannel.configureBlocking(false);
            //注册到选择器上,并让选择器监听read事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • MsgHandler

msgHandler.work方法用来处理业务数据

msgHandler.close方法用来把对象返回到对象池

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

/**
 * 处理业务部分的handler
 */
public class MsgHandler implements Closeable {
    //选择器
    private Selector selector;
    //是否正在运行
    private boolean isRunning=false;

    private SelectionKey self;
    public MsgHandler(){}

    public Selector getSelector() {
        return selector;
    }

    public void setSelector(Selector selector) {
        this.selector = selector;
    }

    public SelectionKey getSelf() {
        return self;
    }

    public void setSelf(SelectionKey self) {
        this.self = self;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    //读取从客户端发来的数据
    public String read(SelectionKey self) {
        try {
            //获取事件发生、代表客户端连接的socketChannel
            SocketChannel socketChannel = (SocketChannel) self.channel();

            //读取通道的数据到缓存,用来记录日志
            ByteBuffer b = ByteBuffer.allocate(1024);
            int read = socketChannel.read(b);
            if(read>0)
                return new String(b.array(), 0, read);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //群发消息(不包含自己)
    public void send(final String msg) {
        System.out.println(Thread.currentThread().getName()+"一共有" + selector.keys().size() + "个连接");
        selector.keys().stream().filter(selectionKey -> self != selectionKey).forEach(selectionKey -> {
            try {
                //群发消息(ServerSocketChannel除外)
                if (selectionKey.channel() instanceof SocketChannel) {
                    //获取代表客户端连接的socketChannel通道
                    SocketChannel socketChannel1 = (SocketChannel) selectionKey.channel();
                    //把缓存内的数据写入到socketChannel通道
                    socketChannel1.write(ByteBuffer.wrap(msg.getBytes()));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
    //获取业务数据
    public void work(String userName, String msg) {
        MsgHandlerPool.getThreadPool().execute(new Worker(this,userName, msg));
    }

    @Override
    public void close() throws IOException {
        MsgHandlerPool.returnBack(this);
    }
}
  • MsgHandlerPool

对象池,包含msgHandler对象池和线程池

import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MsgHandlerPool {
    //线程池
    private static final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    //消息处理对象池
    private static final LinkedList<MsgHandler> msgHandlers=new LinkedList<MsgHandler>();
    static {
        init();
    }
    //初始化:默认初始化10个msgHandler对象到对象池中
    private static void init(){
        for(int i=0;i<10;i++){
            msgHandlers.add(new MsgHandler());
        }
    }

    private MsgHandlerPool(){}
    //获取msgHandler对象
    public static MsgHandler getMsgHandler(){

        if(msgHandlers.size()<=0){
            synchronized (MsgHandlerPool.class){
                if(msgHandlers.size()<=0){
                    init();
                }
            }
        }
        MsgHandler msgHandler = msgHandlers.removeFirst();
        msgHandler.setRunning(true);
        System.out.println(Thread.currentThread().getName()+"拿出一个msgHandler对象,对象池中还有"+msgHandlers.size()+"个对象");
        return msgHandler;
    }
    //回收msgHandler对象
    public static void returnBack(MsgHandler msgHandler){
        if(msgHandler!=null){
            msgHandler.setRunning(false);
            msgHandlers.addLast(msgHandler);
        }
        System.out.println(Thread.currentThread().getName()+"回收一个msgHandler对象,对象池中还有"+msgHandlers.size()+"个对象");
        gc();
    }
    //获取线程池对象
    public static ExecutorService getThreadPool(){
        return cachedThreadPool;
    }
    //回收没有运行的msgHandler
    private static void gc(){
        if(msgHandlers.size()>100){
            synchronized (MsgHandlerPool.class){
                if(msgHandlers.size()>100){
                    System.out.println(Thread.currentThread().getName()+"gc msgHandler对象池");

                    Iterator<MsgHandler> iterator = msgHandlers.iterator();
                    while (iterator.hasNext()){
                        MsgHandler msgHandler = iterator.next();
                        if(!msgHandler.isRunning()){
                            iterator.remove();
                        }
                    }
                }
            }
        }
    }
}
  •  Worker

用来处理业务数据,处理结束后调用msgHandler的send方法返回结束给客户端

public class Worker implements Runnable {
    private String userName;
    private String msg;
    private MsgHandler msgHandler;

    public Worker(MsgHandler msgHandler, String userName, String msg) {
        this.msgHandler=msgHandler;
        this.userName=userName;
        this.msg=msg;
    }

    @Override
    public void run() {
        //模拟业务处理
        String m=userName + "说:"+msg;
        try {
            msgHandler.send(m);
        }finally {
            if(msgHandler!=null)
            try {
                msgHandler.close();
            } catch (IOException e) {
                msgHandler=null;
                e.printStackTrace();
            }
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值