NIO 群聊实现之客户端实现

15,182 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

要知道对好事的称颂过于夸大,也会招来人们的反感轻蔑和嫉妒 —— 培根

NIO 群聊实现之客户端实现

案例背景

    前一篇文章我们实现了群聊系统的服务端的操作,这次我们继续实现客户端的操作,通过两篇系列文章我们将NIO的 API 和组件联合使用,并通过案例群聊服务的实现将技术详细的掌握

开发步骤

  1. 和服务端一样第一步也是创建选择器
  2. 创建客户端 SocketChannel
  3. 设置非阻塞通道SocketChanel
  4. 将客户端的 SocketChannel 注册到 Selector 选择器上面,并将事件设置成监听已读的就绪事件
  5. 根据相应的就绪事件进行逻辑处理【连接事件、可读事件、可写事件等等】
  6. 如果有服务端发来消息的时候,那么需要将其他客户端发来的消息进行对应处理

代码实现

    下面代码进行了上面步骤的实现,进行了客户端的初始化操作,并且进行了服务端的已读就绪事件监听,就绪事件的处理,比如其他客户端发来消息的时候那么就是服务端的进行消息转发,那么此客户端就监听到通道的监听已读事件已就绪

import io.netty.util.CharsetUtil;

import java.io.IOException;
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.util.Iterator;
import java.util.Scanner;

public class ChatClient {
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    public ChatClient() {
        init();
    }

    /**
     * 客户端初始化操作
     */
    private void init() {
        try {
            selector = Selector.open();
            socketChannel = socketChannel
                .open(new InetSocketAddress("127.0.0.1", 8080));
            socketChannel.configureBlocking(false);
            // 将channel注册到selector
            socketChannel.register(selector, SelectionKey.OP_READ);
            // 获取 username 信息
            username = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println("客户端"+ username + " 初始化完成 ...");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 读取从服务器端回复的消息
     */
    public void receiveInfoFromServer() {
        while (true) {
            try {
                int selected = selector.select();
                if (selected > 0) {
                    Iterator<SelectionKey> iterator = selector
                        .selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        if (key.isReadable()) {
                            // 获取socketChannel 通道信息
                            SocketChannel socketChannel = 
                                (SocketChannel) key.channel();
                            // 创建一个buffer缓冲区用来接收消息
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            // 将通道中的数据读取到缓冲区
                            int count = socketChannel.read(buffer);
                            if (count > 0) {
                                System.out.println(
                                    new String(buffer.array(), CharsetUtil.UTF_8));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ChatClient chatClient = new ChatClient();
        // 启动一个线程,监听读取从服务器端发送的数据
        new Thread() {
            @Override
            public void run() {
                chatClient.receiveInfoFromServer();
            }
        }.start();
        // 发送数据给服务器端
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLine()) {
            String msg = sc.nextLine();
            chatClient.sendInfoToServer(msg);
        }
    }
}

结果展示

启动服务端

image.png

启动三个客户端

    第一个客户端
image.png
    此时服务器端收到了客户端 127.0.0.1:51801 的上线通知
image.png
    第二个客户端在启动的时候如果是idea的话需要设置一下可以启动多个才行,不然启动不了
image.pngimage.png
    红框中的方框需要勾选上才行,此时运行显示第二个客户端也初始化完成并上线了
image.png
image.png
    那么第三个也是同理的
image.png
image.png

客户端下线

    当关闭 127.0.0.1:52250 这个客户端的时候服务器端就会收到当前通道关闭的消息,并给出离线的提示
image.png

消息发送结果展示

    此时在客户端 127.0.0.1:51801 的cmd的命令窗口下发送消息 大叫好 我是 51801,此时其他的客户端也收到了消息
image.png
    其他客户端 127.0.0.1:52141 收到消息结果展示
image.png
    此时客户端 127.0.0.1:52141 回消息给客户端 127.0.0.1:51801 你好新同学
image.png
    此时客户端 127.0.0.1:51801 收到消息展示
image.png
    到此就简单的实现了消息的群聊系统

小结

    通过前一篇文章的服务端和这一章的客户端我们就简单的实现了群聊的操作,每个客户端都将消息发送到了服务端,此时服务端收到消息,将消息转发给其他客户端,就实现了群聊天系统,后续我们会将功能完善实现点对点的消息推送