Java NIO实现简单的群聊

NIO服务端

package com.eiot.netty.handler;

import okio.Buffer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * 群聊nio客服端
 * @author laijiangfeng
 * @date 2022/12/7 17:26
 */
public class NIOServer {
    //选择器
    private Selector selector;
    //客服端通道
    private ServerSocketChannel serverSocketChannel;

    //初始化数据
    public NIOServer() {
        try{
            //初始化选择器
            selector=Selector.open();
            //初始化ServerSocketChannel
            serverSocketChannel=ServerSocketChannel.open();
            //绑定端口6666
            serverSocketChannel.socket().bind(new InetSocketAddress(6666));
            //设置非阻塞
            serverSocketChannel.configureBlocking(false);
            //将serverSocketChannel注册到selector
            serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 监听事件发生
     * @throws IOException
     */
    public void listen() throws IOException {
        while (true){
            //判断是否有通道发送事件
            int count = selector.select();
            //大于0有事件发生
            if(count>0){
                //获取有事件发生的key
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    //连接事件
                    if(key.isAcceptable()){
                        SocketChannel channel= serverSocketChannel.accept();
                        //设置非阻塞
                        channel.configureBlocking(false);
                        //将channel注册到选择器,并且关注读事件
                        channel.register(selector,SelectionKey.OP_READ);
                        System.out.println(channel.getRemoteAddress()+"上线了");
                    }

                    //读事件
                    if(key.isReadable()){
                        SocketChannel channel=null;
                        try{
                            //用buffer读取channel的信息
                            channel= (SocketChannel) key.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            int c = channel.read(buffer);
                            //大于0代表有信息
                            if(c>0){
                                String msg= new String(buffer.array());
                                System.out.println();
                                //向所有客户端发送信息
                                for (SelectionKey k : selector.keys()) {
                                    Channel sc = k.channel();
                                    //不向自己发送
                                    if (sc instanceof SocketChannel && sc != channel) {
                                        SocketChannel s = (SocketChannel) sc;
                                        ByteBuffer wrap = ByteBuffer.wrap((channel.getRemoteAddress()+"说:"+msg.trim()).getBytes());
                                        System.out.println("发送数据"+msg.trim());
                                        s.write(wrap);
                                    }
                                }
                            }

                        }catch (IOException e){
                            //读发生异常,通道关闭
                            System.out.println(channel.getRemoteAddress()+"离线了");
                            assert channel != null;
                            key.cancel();
                            channel.close();
                        }

                    }

                    //移除key防止重复处理
                    iterator.remove();
                }
            }
            //一直循环占用cpu高,休息3s可减少cpu消耗
            try{
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }

        }

    }

    /**
     * 主线程启动
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        //开启监听
        NIOServer server = new NIOServer();
        server.listen();
    }
}

NIO客户端

package com.eiot.netty.handler;

import lombok.SneakyThrows;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
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;

/**
 * 群聊nio客户端
 * @author laijiangfeng
 * @date 2022/12/8 11:11
 */
public class NIOClient {
    //选择器
    private Selector selector;
    //客户端SocketChannel
    private SocketChannel socketChannel;

    //初始化连接
    public NIOClient() {
        try {
            //选择器初始化
            selector=Selector.open();
            //连接服务端获取socketChannel
            socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",6666));
            //设置非阻塞
            socketChannel.configureBlocking(false);
            //socketChannel管道注册关注读事件
            socketChannel.register(selector, SelectionKey.OP_READ);
            //连接上提示成功
            System.out.println("登录成功...");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void read() throws IOException {
        //判断是否有管道发送事件
        int c = selector.select();
        //大于0代表有事件发生
        if(c>0){
            //获取有事件发生的key
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            //循环获取key
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                //如果是读事件
                if (key.isReadable()) {
                    //将通道数据读取到buffer
                    SocketChannel channel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    channel.read(buffer);
                    //转成字符串打印
                    String msg = new String(buffer.array());
                    System.out.println(msg.trim());
                }
                //防止循环重复需要移除,不移除导致客户端收不到消息
                iterator.remove();
            }
        }

        //一直循环占用cpu高,休息1s可减少cpu消耗
        try{
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 将消息写入管道
     * @param info
     * @throws IOException
     */
    public void write(String info) throws IOException {
        socketChannel.write(ByteBuffer.wrap(info.trim().getBytes()));
    }

    /**
     * 主线程运行测试
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        //创建一个客户端用一个线程一直循环读取数据
        NIOClient client = new NIOClient();
        new Thread(() -> {
            while (true){
                try {
                    client.read();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //主线程输入消息,并且将消息写入通道
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
            String line = scanner.nextLine();
            client.write(line);
        }
    }


}

结果

猜你喜欢

转载自blog.csdn.net/ysfengshu/article/details/128236594