netty学习笔记03 - NIO编程实例群聊系统

  • 实例要求
  1. 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
  2. 实现多人群聊
  3. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  4. 客户端:通过channel 可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)
  5. 目的:进一步理解NIO非阻塞网络编程机制

代码如下
详细解释看 注释

服务器端:

public class GroupChatServer {
    // 定义属性
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 6667;

    // 初始化工作
    public GroupChatServer() {
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            // 把ServerSocketChannel注册到Selector上并监听accept事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听事件
     */
    public void listen() {
        try {
            while (true) {
                if (selector.select() == 0) {
                    // 没有事件发生
                    continue;
                }
                // 代码走到这表示有事件发生
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey selectionKey = keyIterator.next();
                    // 是连接事件
                    if (selectionKey.isAcceptable()) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        // 设置为非阻塞
                        socketChannel.configureBlocking(false);
                        // 注册到Selector上监听 read事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        // 提示该客户端上线
                        System.out.println(socketChannel.getLocalAddress() + "上线了");
                    }

                    // 是read事件
                    if (selectionKey.isReadable()) {
                        // 专门写一个方法处理reads事件
                        readData(selectionKey);
                    }

                    // //当前的key 删除,防止重复处理
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //读取客户端消息
    private void readData(SelectionKey key) {
        SocketChannel socketChannel = null;
        try {
            // 通过key获取通道
            socketChannel = (SocketChannel) key.channel();
            // 创建缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int count = socketChannel.read(byteBuffer);
            if (count > 0) {
                // 把缓冲区的数据转成字符串
                byte[] array = byteBuffer.array();
                String msg = new String(array);
                // 输出该消息
                System.out.println("from 客户端:" + msg);
                //向其它的客户端转发消息(去掉自己), 专门写一个方法来处理
                sendInfoToOtherClients(msg, socketChannel);
            }
        } catch (IOException e) {
            try {
                System.out.println(socketChannel.getRemoteAddress() + " 离线了..");
                //取消注册
                key.cancel();
                //关闭通道
                socketChannel.close();
            }catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }

    /**
     * 发送消息个其他客户端,排除自己
     * @param msg
     * @param self
     */
    private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
        System.out.println("服务器消息转发中....");
        Set<SelectionKey> keys = selector.keys();
        for (SelectionKey key : keys) {
            Channel targetChannel = key.channel();
            // 如果targetChannel是SocketChannel,并且不是自己,就把消息转发
            if (targetChannel instanceof SocketChannel && targetChannel != self) {
                ((SocketChannel) targetChannel).write(ByteBuffer.wrap(msg.getBytes()));
            }
        }
    }

    public static void main(String[] args) {
        // 创建服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}

客户端:

public class GroupChatClient {
    private static int port = 6667;
    private static String addr = "127.0.0.1";
    private SocketChannel socketChannel;
    private Selector selector;
    private String username;

    public GroupChatClient() {
        try {
            selector = Selector.open();
            // 创建SocketChanel并绑定端口
            socketChannel = SocketChannel.open(new InetSocketAddress(addr, port));
            // 设置为非阻塞状态
            socketChannel.configureBlocking(false);
            // 把socketChannel注册到选择器上并监听read事件
            socketChannel.register(selector, SelectionKey.OP_READ);
            //得到username
            username = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println(username + " is ok...");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 向服务器发送消息
     * @param info
     */
    public void sendInfo(String info) {
        info  = username + "说:" +  info;
        try {
            ByteBuffer byteBuffer = ByteBuffer.wrap(info.getBytes());
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从服务器读取数据
     */
    public void readInfo() {
        try {

            while (true) {
                int select = selector.select();
                if (select == 0) {
                    continue;
                }
                // 代码走到这表示有事件处理
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    // 读事件
                    if (selectionKey.isReadable()) {
                    // 得到相关的通道
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        //得到一个Buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //读取
                        channel.read(buffer);
                        //把读到的缓冲区的数据转成字符串
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //启动我们客户端
        GroupChatClient chatClient = new GroupChatClient();

        //启动一个线程, 每个3秒,读取从服务器发送数据
        new Thread() {
            public void run() {

                while (true) {
                    chatClient.readInfo();
                    try {
                        Thread.currentThread().sleep(3000);
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        //发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextLine()) {
            String s = scanner.nextLine();
            chatClient.sendInfo(s);
        }
    }
}
发布了83 篇原创文章 · 获赞 3 · 访问量 9848

猜你喜欢

转载自blog.csdn.net/fyj13925475957/article/details/104307234