基于NIO的简易群聊系统

服务器端  (最下面有NIO的使用demo)

package me.jar.nio.groupchat;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description
 * @Date 2021/1/24-22:45
 */
public class GroupCharServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 8889;

    public GroupCharServer() {
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            System.out.println("服务器启动失败!" + e.getMessage());
        }
    }

    public void listen() {
        try {
            while (true) {
                int count = selector.select(2000L);
                if (count == 0) {
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        System.out.println("【" + socketChannel.getRemoteAddress() + "】上线了>>>");
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    if (selectionKey.isReadable()) {
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        int length = 0;
                        try {
                            length = channel.read(byteBuffer);
                            String message = "【" + channel.getRemoteAddress() + "】说->" + new String(byteBuffer.array(), 0, length, Charset.forName("UTF-8"));
                            System.out.println(message);
                            sendToOthers(channel, message);
                        } catch (IOException e) {
                            System.out.println("【" + channel.getRemoteAddress() + "】离线了...");
                            selectionKey.cancel();
                            channel.close();
                        }


                    }

                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendToOthers(SocketChannel channel, String message) throws IOException {
        // 转发消息到其他客户端
        Set<SelectionKey> keys = selector.keys();
        for (SelectionKey key : keys) {
            SelectableChannel selectableChannel = key.channel();
            if (selectableChannel instanceof SocketChannel && selectableChannel != channel) {
                SocketChannel socketChannel = (SocketChannel) selectableChannel;
                ByteBuffer messageToSend = ByteBuffer.wrap(message.getBytes(Charset.forName("UTF-8")));
                socketChannel.write(messageToSend);
            }
        }
    }

    public static void main(String[] args) {
        GroupCharServer server = new GroupCharServer();
        server.listen();
    }
}

 

客户端

package me.jar.nio.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @Description
 * @Date 2021/1/24-23:33
 */
public class GroupChatClient {
    private static final String HOST_NAME = "127.0.0.1";
    private static final int HOST_PORT = 8889;
    private Selector selector;
    private SocketChannel socketChannel;

    public GroupChatClient() {
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress(HOST_NAME, HOST_PORT));
            while (!socketChannel.finishConnect()) {
                System.out.println("连接正在建立...");
            }
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            System.out.println("客户端启动失败!" + e.getMessage());
        }
    }

    public void run() {
        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                ByteBuffer byteBuffer = ByteBuffer.wrap(line.getBytes(Charset.forName("UTF-8")));
                try {

                    socketChannel.write(byteBuffer);
                    SocketAddress localAddress = socketChannel.getLocalAddress();
                    System.out.println("【" + line + "】已发送!你的地址->" + localAddress);
                } catch (IOException e) {
                    System.out.println("消息发送失败!" + e.getMessage());
                }
            }
        }).start();
        try {
            while (true) {
                int count = selector.select(2000L);
                if (count == 0) {
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isReadable()) {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        int length = 0;
                        try {
                            length = socketChannel.read(byteBuffer);
                        } catch (IOException e) {
                            System.out.println("服务端断开了..." + e.getMessage());
                        }
                        System.out.println(new String(byteBuffer.array(), 0, length, Charset.forName("UTF-8")));
                    }
                    iterator.remove();
                }
            }
        }catch (IOException e) {
            System.out.println("连接断开..." + e.getMessage());
        }
    }

    public static void main(String[] args) {
        GroupChatClient client = new GroupChatClient();
        client.run();
    }
}

 

NIO的使用demo

服务器端

package me.jar.nio;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description
 * @Date 2021/1/24-16:28
 */
public class NIOServer {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 创建一个Selector对象
        Selector selector = Selector.open();
        // 绑定一个端口,在服务器端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        // 设置为非阻塞(这步很重要也很容易忽略)!
        serverSocketChannel.configureBlocking(false);
        // 把ServerSocketChannel注册到Selector中,关注的事件是OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 循环等待客户端连接
        while (true) {
            // 没有事件发生。等待一秒,如果没有事件发生,continue
            if (selector.select(1000L) == 0) {
                System.out.println("服务器等待了1秒,无连接");
                continue;
            }

            // 如果返回>0,能获取到相关的SelectionKey集合,表示已经获取到关注的事件
            // 通过selectionKey反向获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                // 获取selectionKey
                SelectionKey selectionKey = iterator.next();
                // 根据selectionKey,对应的通道发生的事件做相应处理
                if (selectionKey.isAcceptable()) { // 如果是OP_ACCEPT,有新的客户端连接
                    // 在该客户端生成一个SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println("客户端连接成功,生成了一个socketChannel->" + socketChannel.hashCode());
                    // 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    // 将socketChannel注册到selector,关注事件为OP_READ,同时给socketChannel关联一个Buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                if (selectionKey.isReadable()) { // 发生OP_READ
                    // 通过selectionKey,反向获取对应Channel
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    // 获取到该channel关联的buffer
                    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                    int length = 0;
                    try {
                        length = socketChannel.read(byteBuffer);
                    } catch (IOException e) {
                        System.out.println("客户端可能已经断开!");
                        selectionKey.cancel();
                    }
                    if (length > 0) {
                        System.out.println("from 客户端:" + new String(byteBuffer.array(), 0, length, Charset.forName("UTF-8")));
                    }
                }

                // 手动从集合中移除当前的selectionKey,防止重复操作
                iterator.remove();
            }
        }
    }
}

 

客户端

package me.jar.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

/**
 * @Description
 * @Date 2021/1/24-16:55
 */
public class NIOClient {
    public static void main(String[] args) {
        try {
            // 得到一个网络通道
            SocketChannel socketChannel = SocketChannel.open();
            // 设置非阻塞
            socketChannel.configureBlocking(false);
            // 提供服务器端的ip和端口
            boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 6666));
            if (!connect) {
                while (!socketChannel.finishConnect()) {
                    System.out.println("因为连接需要时间,客户端不会阻塞,可以做其他事情....");
                }
            }

            // 如果连接成功,就发送数据
            String words = "哈哈,让我们学习NIO吧";
            // 将字符串放到buffer中
            ByteBuffer byteBuffer = ByteBuffer.wrap(words.getBytes(Charset.forName("UTF-8")));
            // 发送数据给服务端
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

posted @ 2021-02-21 12:06  hello4world  阅读(79)  评论(0)    收藏  举报