zoukankan      html  css  js  c++  java
  • Java 非阻塞式NIO 案例(实现多人聊天功能)

    一、使用Java NIO完成网络通信的三个核心

      1.通道(Channel):负责连接

            java.nio.channels.Channel 接口:
                  |--SelectableChannel
                      |--SocketChannel
                      |--ServerSocketChannel
                      |--DatagramChannel
     
                      |--Pipe.SinkChannel
                      |--Pipe.SourceChannel

      2.缓冲区(buffer):负责数据存取

      3.选择器(Selector):是SelectableChannel 的多路复用器,用来检测SelectableChannel的IO状态

    案例:使用非阻塞式实现简单的群聊天系统

    一、实现客户端

     1     public static void main(String[] args) throws Exception {
     2         SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
     3 
     4         //2. 切换非阻塞模式
     5         sChannel.configureBlocking(false);
     6 
     7         //3. 分配指定大小的缓冲区
     8         ByteBuffer buf = ByteBuffer.allocate(1024);
     9 
    10         //4. 发送数据给服务端
    11         Scanner scan = new Scanner(System.in);
    12 
    13         while (scan.hasNext()) {
    14             String str = scan.next();
    15             buf.put((new Date().toString() + "
    " + str).getBytes());
    16             buf.flip();
    17             sChannel.write(buf);
    18             buf.clear();
    19         }
    20 
    21         //5. 关闭通道
    22         sChannel.close();
    23     }

    二、实现服务端

        @Test
        public void server() {
            ServerSocketChannel ssChannel = null;
            try {
                ssChannel = ServerSocketChannel.open();
                //配置非阻塞
                ssChannel.configureBlocking(false);
                //绑定连接
                ssChannel.bind(new InetSocketAddress(8989));
    
                Selector selector = Selector.open();
    
                //将通道注册到监听器中,并且制定监听器的监听模式为“接受”
                ssChannel.register(selector, SelectionKey.OP_ACCEPT);
    
                //轮询的选择已经就绪的事件
                while (selector.select() > 0) {
                    //获取当前监听
                    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
    
                    while (it.hasNext()) {
                        //获取准备就绪的事件
                        SelectionKey sk = it.next();
                        if (sk.isAcceptable()) {
                            //如果接受就绪,则获取客户端的连接
                            SocketChannel clientChannel = ssChannel.accept();
    
                            //同样配置成非阻塞式
                            clientChannel.configureBlocking(false);
    
                            //把客户端的连接注册到选择器上
                            clientChannel.register(selector, SelectionKey.OP_READ);
                        } else if (sk.isReadable()) {
                            //如果读取就绪,则获取读取的通道
                            SocketChannel socketChannel = (SocketChannel) sk.channel();
    
                            //配置成非阻塞模式
                            socketChannel.configureBlocking(false);
    
                            //读取数据
    
                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
                            int len = 0;
                            while ((len = socketChannel.read(byteBuffer)) > 0) {
                                byteBuffer.flip();
                                System.out.println(new String(byteBuffer.array(), 0, len));
                                byteBuffer.clear();
                            }
                        }
                        it.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(ssChannel!=null){
                    try {
                        ssChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    注意:这里服务端用到的org.junit.Test;这个包方便测试,客户端因为需要读取输入所以写在Main函数中(@Test方法中测试出来好像不能读取输入)

    需要下载包的地址如下:

    链接:https://pan.baidu.com/s/14ZHHOnAD3ldNVcA3pmCoJQ
    提取码:uqd9

    DatagramChannel(UDP)的使用方法(和上个案例大同小异)

    public static void main(String args[]) {
            DatagramChannel datagramChannel = null;
            try {
                datagramChannel = DatagramChannel.open();
    
                datagramChannel.configureBlocking(false);
    
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
                Scanner scanner = new Scanner(System.in);
    
                while (scanner.hasNext()) {
                    String str = scanner.next();
                    byteBuffer.put(str.getBytes());
                    byteBuffer.flip();
                    datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 9897));
                    byteBuffer.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            finally {
                if(datagramChannel!=null){
                    try {
                        datagramChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        @Test
        public void server(){
            DatagramChannel datagramChannel = null;
            try {
                datagramChannel=DatagramChannel.open();
                datagramChannel.bind(new InetSocketAddress(9897));
    
                datagramChannel.configureBlocking(false);
    
                Selector selector = Selector.open();
                datagramChannel.register(selector, SelectionKey.OP_READ);
    
                while(selector.select()>0){
                    Iterator<SelectionKey> st=selector.selectedKeys().iterator();
    
                    while(st.hasNext()){
                        SelectionKey sk=st.next();
    
                        if(sk.isReadable()){
                            ByteBuffer btf=ByteBuffer.allocate(1024);
    
                            datagramChannel.receive(btf);
    
                            btf.flip();
    
                            System.out.println(new String(btf.array(),0,btf.limit()));
                            btf.clear();
                        }
                    }
                    st.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(datagramChannel!=null){
                    try {
                        datagramChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    Pipe简介

    pipe是两个线程之间单项数据连接,Pipe有两个数据通道,Sign通道负责写入,Source通道负责读取。
    案例如下:
        @Test
        public void test() throws Exception {
            Pipe pipe = Pipe.open();
    
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
            byteBuffer.put("hello world".getBytes());
    
            Pipe.SinkChannel sinkChannel=pipe.sink();
            byteBuffer.flip();
            sinkChannel.write(byteBuffer);
    
    
            //读取
    
            Pipe.SourceChannel sourceChannel =pipe.source();
            byteBuffer.flip();
            int len=sourceChannel.read(byteBuffer);
    
            System.out.println("sourceChanel:"+new String(byteBuffer.array(),0,len));
            byteBuffer.clear();
    
            sinkChannel.close();
    
            sourceChannel.close();
    
        }
    
    
    
     

    谢谢浏览,如有问题直接评论,我会及时更改我的错误。

  • 相关阅读:
    python---redis缓存页面前戏之剖析render源码
    python---redis的python使用
    python---redis中文操作与系统中文检测
    python---redis在windows安装以及测试
    python---基于memcache的自定义session类
    python---memcache使用操作
    python---memcache基本使用以及内部原理
    googlecast
    Android-L-Samples
    libsuperuser
  • 原文地址:https://www.cnblogs.com/huangzhenxiong/p/10372548.html
Copyright © 2011-2022 走看看