美文网首页
网络编程之NIO聊天室

网络编程之NIO聊天室

作者: 帅气的名称被占用 | 来源:发表于2020-03-04 15:55 被阅读0次

    1.创建Nio服务端

    package nio.study;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.Channel;
    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;
    
    import javax.xml.ws.handler.MessageContext.Scope;
    
    /**
     *创建Nio服务端 
     */
    public class NIOServer {
        /**
         *启动 
         * @throws IOException 
         */
        public void start() throws IOException {
            /**
             * 1、 创建selector
             */
            Selector selector = Selector.open();
            
            /**
             * 2、通过ServerScoketChannel创建channel通过 
             */
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            
            /**
             * 3、为channel通道绑定监听端口 
             */
            serverSocketChannel.bind(new InetSocketAddress(8000));
            /**
             * 4、**设置channel为非阻塞状态 
             */
            serverSocketChannel.configureBlocking(false);
            
            
            /**
             * 5、将channel注册到selector上 监听连接事件
             */
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务器启动成功!");
            /**
             * 6、循环等待新接入的连接 
             */
            for(;;) {
                /**
                 * TODO 获取可用channel数量
                 */
                int readyChannels = selector.select();
                /**
                 * TODO 为什么这样?
                 */
                if(readyChannels == 0) continue;
                /**
                 *获取channel可用集合 
                 */
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectedKeys.iterator();
                while(iterator.hasNext()) {
                    /**
                     *selectrionKey实例 
                     */
                    SelectionKey selectionKey = iterator.next();
                    /**
                     *移除Set中的当前selectionKey 
                     */
                    iterator.remove();
                    /**
                     * 7、根据就绪状态,调用对应方法处理业务逻辑 
                     */
                    /**
                     *如果是接入事件 
                     */
                    if(selectionKey.isAcceptable()) {
                        acceptHandler(serverSocketChannel, selector);
                    }
                    /**
                     *如果是可读事件 
                     */
                    if(selectionKey.isReadable()) {
                        readHandler(selectionKey, selector);
                    }
                }
                
            }
        }
        /**
         *接入事件处理 
         * @throws IOException 
         */
        private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) 
                throws IOException {
            /**
             *如果是接入事件,创建socketChannel 
             */
            SocketChannel socketChannel = serverSocketChannel.accept();
            /**
             *将socketChannel设置为非阻塞工作模式 
             */
            socketChannel.configureBlocking(false);
            /**
             *将channel注册到selector上,监听可读事件 
             */
            socketChannel.register(selector, SelectionKey.OP_READ);
            /**
             * 回写客户端提示信息
             */
            socketChannel.write(Charset.forName("UTF-8").
                    encode("你与聊天室的其他人都不是朋友关系,请注意隐私安全"));
        }
        /**
         *可读事件处理 
         * @throws IOException 
         */
        private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
            /**
             *要从 selectionKey中获取已经就绪的channe 
             *SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
             */
            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
             /**
              * 创建Buffer
              */
             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
             /**
              *循环读取客户端信息 
              */
             String request = "";
             while(socketChannel.read(byteBuffer)>0) {
                 /**
                  *切换buffer为读模式 
                  */
                 byteBuffer.flip();
                 /**
                  *读取buffer中的内容 
                  */
                 request += Charset.forName("UTF-8").decode(byteBuffer);
             }
             /**
              * 再次将socketChannel注册到selector上
              */
             socketChannel.register(selector, SelectionKey.OP_READ);
             /**
              * 将客户端发送的请求信息,广播给其他客户端
              */
             if(request.length() > 0) {
                 broadCast(selector, socketChannel, request);
             }
        }
        
        private void broadCast(Selector selector,SocketChannel sourceChannel,String request) {
            /**
             *获取到所有已接入的客户端channel 
             */
            Set<SelectionKey> selectionKeySet = selector.keys();
            /**
             *循环向所有channel广播 
             */
            selectionKeySet.forEach(selectionKey->{
                Channel targetchannel = selectionKey.channel();
                //剔除发消息的客户端
                if(targetchannel instanceof SocketChannel
                        && targetchannel != sourceChannel) {
                    try {
                        ((SocketChannel)targetchannel).
                        write(Charset.forName("UTF-8").encode(request));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        /**
         *主方法 
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            NIOServer server = new NIOServer();
            server.start();
        }
    }
    
    

    2、创建NIO客户端

    package nio.study;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Scanner;
    
    public class NioClient {
        /**
         *启动 
         * @throws IOException 
         */
        public void start(String nickName) throws IOException {
            /**
             *连接服务器端 
             */
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));
            /**
             *接收服务端的响应 
             */
            //新开线程专门接收服务器端的响应数据
            Selector selector = Selector.open();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
            new Thread(new NioClientThreadHandler(selector)).start();;
            
            /**
             *向服务器发送数据
             */
            System.out.println("客户端:");
            Scanner scanner = new Scanner(System.in);
            while(scanner.hasNextLine()) {
                String request = scanner.nextLine();
                if(request != null && request.length() > 0) {
                    socketChannel.write(Charset.forName("UTF-8").encode(nickName+":"+request));
                }
            }
            
        }
        /**
         *主方法 
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
    //      new NioClient().start();
        }
    }
    
    

    3、NIO客户端线程处理类

    package nio.study;
    
    import java.io.IOException;
    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.Set;
    
    public class NioClientThreadHandler implements Runnable {
        private Selector selector;
        
        public NioClientThreadHandler(Selector selector) {
            super();
            this.selector = selector;
        }
    
        @Override
        public void run() {
             for(;;) {
                 try {
                    int readyChannels = selector.select();
                
                    if(readyChannels == 0) continue;
                    /**
                     *获取channel可用集合 
                     */
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectedKeys.iterator();
                    while(iterator.hasNext()) {
                        /**
                         *selectrionKey实例 
                         */
                        SelectionKey selectionKey = iterator.next();
                        /**
                         *移除Set中的当前selectionKey 
                         */
                        iterator.remove();
                         
                        /**
                         *如果是接入事件 
                         */
                        if(selectionKey.isReadable()) {
                            readHandler(selectionKey, selector);
                        }
                        
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
             }
            
        }
        private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
            /**
             *要从 selectionKey中获取已经就绪的channe 
             *SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
             */
            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
             /**
              * 创建Buffer
              */
             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
             /**
              *循环读取客户端信息 
              */
             String response = "";
             while(socketChannel.read(byteBuffer)>0) {
                 /**
                  *切换buffer为读模式 
                  */
                 byteBuffer.flip();
                 /**
                  *读取buffer中的内容 
                  */
                 response += Charset.forName("UTF-8").decode(byteBuffer);
             }
             /**
              * 再次将socketChannel注册到selector上
              */
             socketChannel.register(selector, SelectionKey.OP_READ);
             /**
              * 将服务的信息
              */
             if(response.length() > 0) {
                 System.out.println(response);
             }
        }
    
    }
    
    

    4、创建NioClient多个客户端实现聊天

    package nio.study;
    
    import java.io.IOException;
    
    public class AClient {
        public static void main(String[] args) throws IOException {
            new NioClient().start("AClient");
        }
    }
    package nio.study;
    
    import java.io.IOException;
    
    public class BClient {
        public static void main(String[] args) throws IOException {
            new NioClient().start("BClient");
        }
    }
    package nio.study;
    
    import java.io.IOException;
    
    public class CClient {
        public static void main(String[] args) throws IOException {
            new NioClient().start("CClient");
        }
    }
    
    

    相关文章

      网友评论

          本文标题:网络编程之NIO聊天室

          本文链接:https://www.haomeiwen.com/subject/efxblhtx.html