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

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

架构图:

 

服务端代码改造

1 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;

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

    //处理连接的acceptor
    private Acceptor acceptor;
    //处理业务部分的handler
    private MsgHandler msgHandler;
    
    //初始化服务端
    {
        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 MsgHandler getMsgHandler() {
        return msgHandler;
    }

    public void setMsgHandler(MsgHandler msgHandler) {
        this.msgHandler = msgHandler;
    }

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

                //群发消息(不包含自己)
                if(msgHandler!=null) {

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

                    //处理消息
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    String userName ="未知";
                    try {
                        userName = socketChannel.getRemoteAddress().toString().substring(11);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    String sendMsg=msgHandler.getSendMsg(userName,msg);

                    //发送消息
                    msgHandler.send(selector, selectionKey, sendMsg);
                }
            }
            //移出处理完事件的selectionKey
            iterator.remove();
        }
    }
    //事件监听:accept和read事件
    public void select(){
        while (true) {
            try {
                System.out.println("开始监听事件");
                //当没有事件发生时会阻塞在此位置,也可设置不阻塞或阻塞超时时间
                selector.select();

                //事件分发
                dispatch();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        //创建服务端
        NIOReactorServer server=new NIOReactorServer();
        //指定连接处理器
        server.setAcceptor(new Acceptor());
        //指定发送消息处理器
        server.setMsgHandler(new MsgHandler());
        //开始监听
        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 {
            //使用serverSocketChannel分配一个代表客户端连接的socketChannel
            SocketChannel socketChannel = serverSocketChannel.accept();
            //设置为非阻塞式
            socketChannel.configureBlocking(false);
            //注册到选择器上,并让选择器监听read事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

MsgHandler

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

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

    //群发消息(不包含自己)
    public void send(final Selector selector,final SelectionKey self, final String msg) {
        System.out.println("一共有" + 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 String getSendMsg(String userName, String msg) {
        return userName + "说:"+msg;
    }
}

客户端(未改动)

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class NIOClient {
    // 客户端socketChannel
    private SocketChannel socketChannel;
    {
        try {
            //创建socketChannel
            socketChannel=SocketChannel.open();
            //设置非阻塞式
            socketChannel.configureBlocking(false);
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);

            // 创建连接
            if(!socketChannel.connect(inetSocketAddress)){
                while (!socketChannel.finishConnect()){
                    System.out.println("客户端不会阻塞,可以做其他工作");
                }
            }
            //连接成功后开启一个线程定时2秒接收服务器转发的消息
            receiveMsg();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 收消息线程
    private void receiveMsg(){
        new Thread(()->{
            System.out.println("开始收取信息...");
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true) {
                try {
                    //把通道的数据写入缓存buffer
                    int read = socketChannel.read(buffer);
                    if(read>0)
                        System.out.println(new String(buffer.array(),0,read));

                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    //清空缓存buffer
                    buffer.clear();
                }
            }
        }).start();
    }
    //发送消息
    public void sendMsg(String msg){
        try {
            //发送消息
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        NIOClient client=new NIOClient();

        // 循环发送消息
        Scanner scanner=new Scanner(System.in);
        while (scanner.hasNextLine()) {
            try {
                client.sendMsg(scanner.nextLine());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值