4.トンの弟は、Java NIOの網状シリーズは(自分の中毒とのトーク)グループチャットを達成しました

NIO

こんにちは、私は、ここで、これはシリーズネッティーで4番目の兄トンです。

いいえから私の公衆へようこそトンの弟は、ソースコードの読み取りの系統的な研究ソースコード&アーキテクチャの知識を。

簡単な紹介

我々は、Java BIO / NIO / AIOの物語の中で一緒に学ぶ最後の章では、この章では、より一緒に多くの中毒性の「グループチャットシステム」を語る達成無実のNIOを使用するために皆をもたらすでしょう。

ビジネスロジック解析

まずは、ファンクションポイントのグループチャットを分析してみましょう:

(1)は、グループチャットに参加し、他のユーザーに通知します。

話をすると、他の人に知らせるため(2);

(3)グループチャットなどを通知します。

シンプルなグループチャットシステムのほぼ三つの機能は、ユーザーが自動的に彼にユーザーIDが割り当てられているチャットグループに参加する際に、レコードのユーザ情報を容易にするために、十分です。

ビジネス実装

コードの場合:

// 这是一个内部类
private static class ChatHolder {
    // 我们只用了一个线程,用普通的HashMap也可以
    static final Map<SocketChannel, String> USER_MAP = new ConcurrentHashMap<>();

    /**
     * 加入群聊
     * @param socketChannel
     */
    static void join(SocketChannel socketChannel) {
        // 有人加入就给他分配一个id,本文来源于公从号“彤哥读源码”
        String userId = "用户"+ ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
        send(socketChannel, "您的id为:" + userId + "\n\r");

        for (SocketChannel channel : USER_MAP.keySet()) {
            send(channel, userId + " 加入了群聊" + "\n\r");
        }

        // 将当前用户加入到map中
        USER_MAP.put(socketChannel, userId);
    }

    /**
     * 退出群聊
     * @param socketChannel
     */
    static void quit(SocketChannel socketChannel) {
        String userId = USER_MAP.get(socketChannel);
        send(socketChannel, "您退出了群聊" + "\n\r");
        USER_MAP.remove(socketChannel);

        for (SocketChannel channel : USER_MAP.keySet()) {
            if (channel != socketChannel) {
                send(channel, userId + " 退出了群聊" + "\n\r");
            }
        }
    }

    /**
     * 扩散说话的内容
     * @param socketChannel
     * @param content
     */
    public static void propagate(SocketChannel socketChannel, String content) {
        String userId = USER_MAP.get(socketChannel);
        for (SocketChannel channel : USER_MAP.keySet()) {
            if (channel != socketChannel) {
                send(channel, userId + ": " + content + "\n\r");
            }
        }
    }

    /**
     * 发送消息
     * @param socketChannel
     * @param msg
     */
    static void send(SocketChannel socketChannel, String msg) {
        try {
            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
            writeBuffer.put(msg.getBytes());
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}复制代码

サーバ・コード

サーバーのコードが直接NIO前章を実施し、唯一のビジネスロジックは、ここでタイムリーに対応するイベントに挿入し、メイクの上に実装されています。

(1)接続が確立される場合、すなわち、命令は、グループチャットに参加するために、イベントを受け入れます。

に話をする人を示す、データを読み出す場合(2)、すなわち、イベントを読み出します。

(3)接続が切断されたときに、グループチャットのアウトを示します。

OKは、直接コードで、最後の章のコードを区別するために、トングゴシックが追加いくつかのマーカーを意図しました:

public class ChatServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        // 将accept事件绑定到selector上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 阻塞在select上
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 遍历selectKeys
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 如果是accept事件
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = ssc.accept();
                    System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    // 加入群聊,本文来源于公从号“彤哥读源码”
                    ChatHolder.join(socketChannel);
                } else if (selectionKey.isReadable()) {
                    // 如果是读取事件
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    // 将数据读入到buffer中
                    int length = socketChannel.read(buffer);
                    if (length > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        // 将数据读入到byte数组中
                        buffer.get(bytes);

                        // 换行符会跟着消息一起传过来
                        String content = new String(bytes, "UTF-8").replace("\r\n", "");
                        if (content.equalsIgnoreCase("quit")) {
                            // 退出群聊,本文来源于公从号“彤哥读源码”
                            ChatHolder.quit(socketChannel);
                            selectionKey.cancel();
                            socketChannel.close();
                        } else {
                            // 扩散,本文来源于公从号“彤哥读源码”
                            ChatHolder.propagate(socketChannel, content);
                        }
                    }
                }
                iterator.remove();
            }
        }
    }
}复制代码

テスト

オープン4 XSHELLクライアントが接続されているtelnet 127.0.0.1 8080、あなたはグループチャットを開始することができます。

NIO

自分のチャットも中毒性あるとトング弟が自分自身を発見し、死ぬことを、完全に停止することはできません、私はチャットから行ってきました^^

概要

「グループチャットシステム」を達成するために一緒にみんなと一緒にこの記事トンの弟は、コードの100行についてのコメントを削除し、それは非常に単純ではないでしょうか?これは、私もはまって書か見つかりプログラミングネットワークNIOのネットワークプログラミングの魅力です^^

問題

どちらの章では、我々はあなたがそれを達成する方法を知って、NIOクライアントで達成していませんか?

ヒント:サーバーは、のServerSocketChannelを持っていることが必要であるので、イベントを受け入れることを聞く必要があり、クライアントはその上のSocketChannelを直接使用するので、のSocketChannelが接続に相当し、サーバーに直接接続されています。

最後に、公共のもあるから私の電話番号に来て歓迎し、ソースコードの読み取りトンの弟の系統的な研究ソースコード&アーキテクチャの知識を。

コード

おすすめ

転載: juejin.im/post/5dd4122a51882567490f95ed