NIO实现群聊系统

目标:实现一个多人在线群聊系统

服务端

package com.it.chat;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class Server {

    private Selector selector;

    private ServerSocketChannel serverSocketChannel;

    private Integer port;


    /**
     * 构造方法
     *
     * @throws Exception
     */
    public Server() throws Exception {
        port = 8080;
        // 获取通道
        serverSocketChannel = ServerSocketChannel.open();
        // 设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 绑定端口
        serverSocketChannel.bind(new InetSocketAddress(port));
        // 获取选择器
        selector = Selector.open();
        // 将通道注册到选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 处理所有的事件
     *
     * @throws Exception
     */
    public void handleEvent() throws Exception {
        while (selector.select() > 0) {
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while (selectionKeyIterator.hasNext()) {
                SelectionKey selectionKey = selectionKeyIterator.next();
                // 处理接收事件
                if (selectionKey.isAcceptable()) {
                    handleAcceptEvent();
                } else if (selectionKey.isReadable()) {
                    // 处理消息,广播,将消息转发给其他线上人员
                    handleBroadCast(selectionKey.channel());
                }
                // 当前事件处理完毕,移除事件
                selectionKeyIterator.remove();
            }
        }
    }

    /**
     * 处理接收事件
     *
     * @throws Exception
     */
    public void handleAcceptEvent() throws Exception {
        // 获取通道
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        // 将通道注册到选择器上,并监听读事件
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    /**
     * 广播消息,将消息发送给其他线上客户端
     *
     * @param channel 源通道
     * @throws Exception
     */
    public void handleBroadCast(Channel channel) throws Exception {
        SocketChannel originChannel = (SocketChannel) channel;
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        // 从通道中读取数据
        int read = originChannel.read(byteBuffer);
        // 获取发送消息的客户端host地址
        String remoteAddress = originChannel.getRemoteAddress().toString().substring(1);
        System.out.println(remoteAddress + " say: " + new String(byteBuffer.array(), 0, read));
        byteBuffer.flip();
        Set<SelectionKey> selectionKeys = selector.keys();
        // 循环,将消息发送给所有线上客户端
        for (SelectionKey selectionKey : selectionKeys) {
            Channel targetChannel = selectionKey.channel();
            if (targetChannel instanceof SocketChannel && originChannel != targetChannel) {
                SocketChannel destinationChannel = (SocketChannel) targetChannel;
                // 将数据从缓冲区写入目标通道
                destinationChannel.write(byteBuffer);
            }
        }
        byteBuffer.clear();
    }

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        server.handleEvent();
    }
}

客户端

package com.it.chat;

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.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class Client {
    private Selector selector;

    private SocketChannel socketChannel;

    private String host;

    private Integer port;

    private String username;


    /**
     * 构造函数
     *
     * @throws Exception
     */
    public Client() throws Exception {
        // 服务器地址
        host = "127.0.0.1";
        port = 8080;
        // 获取通道
        socketChannel = SocketChannel.open(new InetSocketAddress(host, port));
        socketChannel.configureBlocking(false);
        // 获取选择器
        selector = Selector.open();
        // 将通道注册到选择器上,并监听读事件
        socketChannel.register(selector, SelectionKey.OP_READ);
        // 获取当前客户端的host地址
        username = socketChannel.getRemoteAddress().toString().substring(1);
    }

    /**
     * 发送消息
     *
     * @param message 消息
     * @throws Exception
     */
    public void sendMessage(String message) throws Exception {
        socketChannel.write(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 接收消息
     *
     * @throws Exception
     */
    public void acceptMessage() throws Exception {
        // 轮询,监听事件
        while (selector.select() > 0) {
            // 获取所有处于就绪状态的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            // 获取事件
            while (selectionKeyIterator.hasNext()) {
                SelectionKey selectionKey = selectionKeyIterator.next();
                // 如果当前事件是读事件
                if (selectionKey.isReadable()) {
                    // 获取当前事件对应的通道
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    int length;
                    // 从通道中读取数据到缓冲区
                    while ((length = channel.read(byteBuffer)) > 0) {
                        byteBuffer.flip();
                        // 从缓冲区中读取数据,输出到控制台
                        System.out.println("【acceptMessage】" + username + " say:" + new String(byteBuffer.array(), 0, length));
                        byteBuffer.clear();
                    }
                }
                // 当前事件处理完毕,移除当前事件
                selectionKeyIterator.remove();
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Client client = new Client();
        new Thread(() -> {
            while (true) {
                try {
                    client.acceptMessage();
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            client.sendMessage(scanner.nextLine());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值