NIO之简单群聊系统

NIO学习之群聊系统

服务基础抽象类

package com.dawn.yi.netty.group;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author: yi
 * @version: 1.0.0
 * @create: 2021/8/9 16:20
 * @description: 抽象服务器
 */
public abstract class AbstractServer {

    /**
     * 多路复用器 当前服务器要注册到那个selector中
     */
    protected final Selector selector;

    /**
     * ip
     */
    protected final String host;

    /**
     * 端口
     */
    protected final int port;

    protected AbstractServer(String host, int port) throws IOException {
        this.host = host;
        this.port = port;
        this.selector = Selector.open();
    }

    /**
     * 初始化操作
     *
     * @return AbstractBaseGroupChat
     */
    protected abstract AbstractServer initialize();

    /**
     * 启动
     */
    protected abstract void start();

    /**
     * 处理通道中发生的事件
     */
    protected void handleHappenedEvent() {
        // 1. 获取所有发生事件的SelectionKey
        Set<SelectionKey> happenEventSelectionKeys = selector.selectedKeys();
        // 2. 使用迭代器遍历
        Iterator<SelectionKey> iterator = happenEventSelectionKeys.iterator();
        SelectionKey happenEventSelectionKey;
        while (iterator.hasNext()) {
            happenEventSelectionKey = iterator.next();
            if (happenEventSelectionKey.isAcceptable()) {
                accept();
            }
            if (happenEventSelectionKey.isWritable()) {
                write(happenEventSelectionKey);
            }
            if (happenEventSelectionKey.isReadable()) {
                read(happenEventSelectionKey);
            }
            // 处理完事件后 需要从Set<SelectionKey>中移除已经处理过的SelectionKey
            iterator.remove();
        }

    }

    /**
     * 建立连接操作
     */
    protected abstract void accept();

    /**
     * 读操作
     *
     * @param selectionKey 当前要操作的selectionKey
     */
    protected abstract void read(SelectionKey selectionKey);

    /**
     * 写操作
     *
     * @param selectionKey 当前要操作的selectionKey
     */
    protected abstract void write(SelectionKey selectionKey);

    /**
     * 获取selectionKey的通道
     *
     * @param selectionKey selectionKey
     * @return SocketChannel
     */
    protected SocketChannel getSocketChannel(SelectionKey selectionKey) {
        return (SocketChannel) selectionKey.channel();
    }

    /**
     * 获取Byte缓冲池
     *
     * @param capacity 缓冲池容量
     * @return ByteBuffer
     */
    protected ByteBuffer getByteBuffer(int capacity) {
        return ByteBuffer.allocate(capacity);
    }

    /**
     * 离线
     *
     * @param selectionKey 要离线的selectionKey
     * @param channel      要关闭的通道
     */
    protected void leave(SelectionKey selectionKey, SocketChannel channel) throws IOException {
        // 取消注册
        selectionKey.channel();
        // 关闭通道
        channel.close();
    }
}

服务端

package com.dawn.yi.netty.group;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;

/**
 * @author: yi
 * @version: 1.0.0
 * @create: 2021/8/2 13:56
 * @description: 群聊系统服务器端 主要用来监听OP_ACCEPT事件
 */
public class GroupChatServer extends AbstractServer {

    /**
     * 服务端通道
     */
    private final ServerSocketChannel server;

    public GroupChatServer(ServerSocketChannel server) throws IOException {
        super("127.0.0.1", 51225);
        this.server = server;
    }

    public static void main(String[] args) throws IOException {
        new GroupChatServer(ServerSocketChannel.open()).initialize().start();
    }

    @Override
    protected AbstractServer initialize() {
        try {
            // 绑定端口
            server.socket().bind(new InetSocketAddress(port));
            // 设置非阻塞
            server.configureBlocking(false);
            // 注册到selector上 并设置监听的事件
            server.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }

    /**
     * 服务器启动 循环监听事件
     */
    @Override
    public void start() {
        System.out.println("服务器启动。。。");
        for (; ; ) {
            // 等待2秒 如果没有事件发生就继续获取
            try {
                if (selector.select(2000) == 0) {
                    System.out.println("服务器等待了2秒,没有客户端连接");
                    continue;
                }
                // 如果有事件发生
                handleHappenedEvent();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 建立连接操作
     */
    @Override
    protected void accept() {
        try {
            // 1. 创建一个客户端通道
            SocketChannel client = server.accept();
            client.configureBlocking(false);
            System.out.printf("客户端%s连接%n", client.getRemoteAddress());
            // 2. 将该连接注册到Selector中 并为该通道绑定它要监听的事件为OP_READ 同时 为其绑定一个缓冲区(共享数据)
            client.register(selector, SelectionKey.OP_READ);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void read(SelectionKey selectionKey) {
        // 1. 获取通道
        SocketChannel channel = getSocketChannel(selectionKey);
        // 2. 获取缓冲区
        ByteBuffer buffer = getByteBuffer(2048);
        // 3. 从通道中读取数据并放到缓存区中
        try {
            int count = channel.read(buffer);
            if (count > 0) {
                String message = new String(buffer.array());
                System.out.printf("客户端%s发来的数据:%s%n", channel.getRemoteAddress(), message);
                buffer.clear();
                toForward(String.format("收到客户端%s的消息:%s", channel.getRemoteAddress(), message), selectionKey);
            }
        } catch (IOException e) {
            try {
                System.out.printf("客户端%s已离线。。。%n", channel.getRemoteAddress());
                // 进行离线操作
                leave(selectionKey, channel);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    @Override
    protected void write(SelectionKey selectionKey) {

    }

    /**
     * 将消息转发给其他客户端
     *
     * @param message 消息
     * @param fromKey 消息来源
     */
    private void toForward(String message, SelectionKey fromKey) {
        selector.keys().stream()
                // 排除自己 以及ServerSocketChannel
                .filter(key -> key.channel() instanceof SocketChannel && key != fromKey)
                .forEach(key -> {
                    try {
                        // 获取目标通道 并 将信息通过缓存区写到通道中
                        getSocketChannel(key).write(ByteBuffer.wrap(message.getBytes()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }
}

客户端

package com.dawn.yi.netty.group;


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

/**
 * @author: yi
 * @version: 1.0.0
 * @create: 2021/8/9 17:11
 * @description: TODO
 */
public class GroupChatClient extends AbstractServer {

    /**
     * 客户端通道
     */
    private final SocketChannel client;

    protected GroupChatClient(SocketChannel client) throws IOException {
        super("127.0.0.1", 51225);
        this.client = client;
    }

    public static void main(String[] args) throws IOException {
        new GroupChatClient(SocketChannel.open()).initialize().start();
    }

    @Override
    protected AbstractServer initialize() {
        try {
            // 建立连接
            client.connect(new InetSocketAddress(host, port));
            // 设置非阻塞
            client.configureBlocking(false);
            // 注册到selector上
            client.register(selector, SelectionKey.OP_READ);
            // 判断是否连接成功
            if (!client.isConnected()) {
                // 如果没有连接成功就进行提示
                for (; ; ) {
                    if (!client.finishConnect()) {
                        System.out.println("客户端连接需要一段时间。。。");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }

    @Override
    protected void start() {
        System.out.println("客户端启动。。。");
        // 另开线程处理读事件
        CompletableFuture.runAsync(() -> {
            for (; ; ) {
                // 等待2秒 如果没有事件发生就继续获取
                try {
                    if (selector.select(2000) == 0) {
                        continue;
                    }
                     // 如果有事件发生
                	handleHappenedEvent();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        // 向服务器发送数据
        Scanner in = new Scanner(System.in);
        while (in.hasNextLine()) {
            try {
                System.out.println("请输入: ");
                client.write(ByteBuffer.wrap(in.nextLine().getBytes()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void read(SelectionKey selectionKey) {
        SocketChannel channel = getSocketChannel(selectionKey);
        ByteBuffer buffer = getByteBuffer(2048);
        try {
            int count = channel.read(buffer);
            if (count > 0) {
                System.out.println(new String(buffer.array()));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void accept() {

    }

    @Override
    protected void write(SelectionKey selectionKey) {

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值