最新公告
  • 欢迎您光临极客文库,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 简介

    NIO,BIO的基础知识这里就不过多赘述,本站也有很多文章讲述,自行搜索,我们这里只简单对比总结一下NIO和BIO的特点。

    BIO中的阻塞

    • ServerSocket.accept()
    • InputStream.read,OutPutStream.write
    • 无法在同一个线程里处理多个Stream I/O

    非阻塞式NIO

    • 使用Channel代替Steam
    • 使用Selecter监控多条Channel
    • 可以在一个线程里处理多个Channel I/O

    NIO中重要的概念Channel和Buffer

    Channel的基本操作总结图

    几个重要的Channel

    • FileChannel
    • ServerSocketChannel
    • SocketChannel

    NIO编程模型

    基础知识就讲到这里,更多相关知识欢迎在本站【极客文库】搜素。

    效果演示

    本节我们同样实现多人在线聊天室,功能大体上和之前的章节相同

    https://www.jikewenku.com/29447.html

    https://www.jikewenku.com/29442.html

    只不过本节换成NIO即同步非阻塞IO来实现

    首先同样启动一个服务端,两个客户端,服务端将打印客户端连接的信息

    然后用客户端分别发送消息

    客户端输入”quit”退出

    代码演示

    服务端代码

    import java.io.Closeable;
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.*;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.util.Set;
    
    /**
     * @author geekerstar
     * @description 服务端
     */
    public class ChatServer {
    
        private static final int DEFAULT_PORT = 8888;
        private static final String QUIT = "quit";
        private static final int BUFFER = 1024;
    
        private ServerSocketChannel server;
        private Selector selector;
        private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
        private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
        private Charset charset = StandardCharsets.UTF_8;
        private int port;
    
        public ChatServer() {
            this(DEFAULT_PORT);
        }
    
        public ChatServer(int port) {
            this.port = port;
        }
    
        private void start() {
            try {
                server = ServerSocketChannel.open();
                server.configureBlocking(false);
                server.socket().bind(new InetSocketAddress(port));
    
                selector = Selector.open();
                server.register(selector, SelectionKey.OP_ACCEPT);
                System.out.println("服务器启动, 监听端口:" + port + "...");
    
                while (true) {
                    selector.select();
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    for (SelectionKey key : selectionKeys) {
                        // 处理被触发的事件
                        handles(key);
                    }
                    selectionKeys.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                close(selector);
            }
        }
    
        private void handles(SelectionKey key) throws IOException {
            // ACCEPT事件 - 和客户端建立了连接
            if (key.isAcceptable()) {
                ServerSocketChannel server = (ServerSocketChannel) key.channel();
                SocketChannel client = server.accept();
                client.configureBlocking(false);
                client.register(selector, SelectionKey.OP_READ);
                System.out.println(getClientName(client) + "已连接");
            }
            // READ事件 - 客户端发送了消息
            else if (key.isReadable()) {
                SocketChannel client = (SocketChannel) key.channel();
                String fwdMsg = receive(client);
                if (fwdMsg.isEmpty()) {
                    // 客户端异常
                    key.cancel();
                    selector.wakeup();
                } else {
                    System.out.println(getClientName(client) + ":" + fwdMsg);
                    forwardMessage(client, fwdMsg);
    
                    // 检查用户是否退出
                    if (readyToQuit(fwdMsg)) {
                        key.cancel();
                        selector.wakeup();
                        System.out.println(getClientName(client) + "已断开");
                    }
                }
    
            }
        }
    
        private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
            for (SelectionKey key : selector.keys()) {
                Channel connectedClient = key.channel();
                if (connectedClient instanceof ServerSocketChannel) {
                    continue;
                }
    
                if (key.isValid() && !client.equals(connectedClient)) {
                    wBuffer.clear();
                    wBuffer.put(charset.encode(getClientName(client) + ":" + fwdMsg));
                    wBuffer.flip();
                    while (wBuffer.hasRemaining()) {
                        ((SocketChannel) connectedClient).write(wBuffer);
                    }
                }
            }
        }
    
        private String receive(SocketChannel client) throws IOException {
            rBuffer.clear();
            while (client.read(rBuffer) > 0) ;
            rBuffer.flip();
            return String.valueOf(charset.decode(rBuffer));
        }
    
        private String getClientName(SocketChannel client) {
            return "客户端[" + client.socket().getPort() + "]";
        }
    
        private boolean readyToQuit(String msg) {
            return QUIT.equals(msg);
        }
    
        private void close(Closeable closable) {
            if (closable != null) {
                try {
                    closable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            ChatServer chatServer = new ChatServer(7777);
            chatServer.start();
        }
    }
    
    

     

    客户端代码

    import java.io.Closeable;
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ClosedSelectorException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.util.Set;
    
    /**
     * @author geekerstar
     * @description
     */
    public class ChatClient {
    
        private static final String DEFAULT_SERVER_HOST = "127.0.0.1";
        private static final int DEFAULT_SERVER_PORT = 8888;
        private static final String QUIT = "quit";
        private static final int BUFFER = 1024;
    
        private String host;
        private int port;
        private SocketChannel client;
        private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
        private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
        private Selector selector;
        private Charset charset = StandardCharsets.UTF_8;
    
        public ChatClient() {
            this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
        }
    
        public ChatClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public boolean readyToQuit(String msg) {
            return QUIT.equals(msg);
        }
    
        private void close(Closeable closable) {
            if (closable != null) {
                try {
                    closable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void start() {
            try {
                client = SocketChannel.open();
                client.configureBlocking(false);
    
                selector = Selector.open();
                client.register(selector, SelectionKey.OP_CONNECT);
                client.connect(new InetSocketAddress(host, port));
    
                while (true) {
                    selector.select();
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    for (SelectionKey key : selectionKeys) {
                        handles(key);
                    }
                    selectionKeys.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClosedSelectorException e) {
                // 用户正常退出
            } finally {
                close(selector);
            }
    
        }
    
        private void handles(SelectionKey key) throws IOException {
            // CONNECT事件 - 连接就绪事件
            if (key.isConnectable()) {
                SocketChannel client = (SocketChannel) key.channel();
                if (client.isConnectionPending()) {
                    client.finishConnect();
                    // 处理用户的输入
                    new Thread(new UserInputHandler(this)).start();
                }
                client.register(selector, SelectionKey.OP_READ);
            }
            // READ事件 -  服务器转发消息
            else if (key.isReadable()) {
                SocketChannel client = (SocketChannel) key.channel();
                String msg = receive(client);
                if (msg.isEmpty()) {
                    // 服务器异常
                    close(selector);
                } else {
                    System.out.println(msg);
                }
            }
        }
    
        public void send(String msg) throws IOException {
            if (msg.isEmpty()) {
                return;
            }
    
            wBuffer.clear();
            wBuffer.put(charset.encode(msg));
            wBuffer.flip();
            while (wBuffer.hasRemaining()) {
                client.write(wBuffer);
            }
    
            // 检查用户是否准备退出
            if (readyToQuit(msg)) {
                close(selector);
            }
        }
    
        private String receive(SocketChannel client) throws IOException {
            rBuffer.clear();
            while (client.read(rBuffer) > 0) ;
            rBuffer.flip();
            return String.valueOf(charset.decode(rBuffer));
        }
    
        public static void main(String[] args) {
            ChatClient client = new ChatClient("127.0.0.1", 7777);
            client.start();
        }
    }
    
    

     

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    /**
     * @author geekerstar
     * @description
     */
    public class UserInputHandler implements Runnable {
    
        private ChatClient chatClient;
    
        public UserInputHandler(ChatClient chatClient) {
            this.chatClient = chatClient;
        }
    
        @Override
        public void run() {
            try {
                // 等待用户输入消息
                BufferedReader consoleReader =
                        new BufferedReader(new InputStreamReader(System.in));
                while (true) {
                    String input = consoleReader.readLine();
    
                    // 向服务器发送消息
                    chatClient.send(input);
    
                    // 检查用户是否准备退出
                    if (chatClient.readyToQuit(input)) {
                        break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
    极客文库 » 基于NIO实现的简易多人在线聊天室客户端和服务端

    常见问题FAQ

    如果资源链接失效了怎么办?
    本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
    如果用户分享的资源与描述不符怎么办?
    可以联系客服QQ:2580505920,如果要求合理可以安排退款或者退赞助积分。
    如何分享个人资源获取赞助积分或其他奖励?
    本站用户可以分享自己的资源,但是必须保证资源没有侵权行为。点击个人中心,根据操作填写并上传即可。资源所获收益完全归属上传者,每周可申请提现一次。
    如果您发现了本资源有侵权行为怎么办?
    及时联系客服QQ:2580505920,核实予以删除。

    Leave a Reply

    Hi, 如果你对这款资源有疑问,可以跟我联系哦!

    联系发布者

    Leave a Reply

    Hi, 如果你对这款资源有疑问,可以跟我联系哦!

    联系发布者
    • 108会员总数(位)
    • 3695资源总数(个)
    • 3本周发布(个)
    • 0 今日发布(个)
    • 181稳定运行(天)

    欢迎加入「极客文库」,成为原创作者从这里开始!

    立即加入 了解更多
    成为赞助用户享有更多特权立即升级