• 9、NIO--阻塞式


    使用NIO完成网络通信的三个核心:

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

      java.nio.channels.Channel接口:

        SelectableChannel抽象类:有一下几个实现类

          SocketChannel      TCP

          ServerSocketChannel  TCP

          DategramChannel      UDP

          Pipe.SinkChannel

          Pipe.SourceChannel

    2、缓冲区(Buffer):负责数据的存取

    3、选择器(Selector):式SelectableChannel的多路复用器,用于监控SelectableChannel的IO状况

    代码实例:

        //服务端
        @Test
        public void server() throws IOException{
            
            //1、获取异常
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            
            //2、绑定连接
            serverSocketChannel.bind(new InetSocketAddress(8081));
        
            //3、获取客户端连接的通道
            SocketChannel sockChannel = serverSocketChannel.accept();
            
            //4、分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
            
            //5、接收客户端的数据保存到本地
            FileChannel outChannel = FileChannel.open(Paths.get("d:\aaa.jpg"), StandardOpenOption.WRITE
                                ,StandardOpenOption.CREATE,StandardOpenOption.READ);
            
            while(sockChannel.read(buf) != -1){
                buf.flip();
                outChannel.write(buf);
                buf.clear();
            }
            
            serverSocketChannel.close();
            outChannel.close();
            sockChannel.close(); 
        }
        //客户端
        @Test
        public void client(){
            
            //1、获取通道
            SocketChannel socketChannel = null;
            
            FileChannel inChannel = null;
            try {
                socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8081));
                
                //2、分配指定大小的缓冲区
                ByteBuffer buf = ByteBuffer.allocate(1024);
                
                //3、读取本地文件,并且发送
                inChannel = FileChannel.open(Paths.get("d:\a.jpg"), StandardOpenOption.READ);
                
                while(inChannel.read(buf) != -1){
                    buf.flip();
                    socketChannel.write(buf);
                    buf.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                    try {
                        socketChannel.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    try {
                        inChannel.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
            }
        }

    先开启服务端在开启客户端:

    阻塞式测试:(服务端给客户端回复响应信息)

    @Test
        public void server() throws IOException{
            
            //1、获取异常
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            
            //2、绑定连接
            serverSocketChannel.bind(new InetSocketAddress(8081));
        
            //3、获取客户端连接的通道
            SocketChannel sockChannel = serverSocketChannel.accept();
            
            //4、分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
            
            //5、接收客户端的数据保存到本地
            FileChannel outChannel = FileChannel.open(Paths.get("d:\aaaa.jpg"), StandardOpenOption.WRITE
                                ,StandardOpenOption.CREATE,StandardOpenOption.READ);
            
            while(sockChannel.read(buf) != -1){
                buf.flip();
                outChannel.write(buf);
                buf.clear();
            }
            
            //发送反馈信息
            buf.put("服务端接收图片成功".getBytes());
            buf.flip();
            sockChannel.write(buf);
            
            serverSocketChannel.close();
            outChannel.close();
            sockChannel.close();
        }

    客户端:

        //客户端
        @Test
        public void client(){
            //1、获取通道
            SocketChannel socketChannel = null;
            FileChannel inChannel = null;
            try {
                socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8081));
                
                //2、分配指定大小的缓冲区
                ByteBuffer buf = ByteBuffer.allocate(1024);
                
                //3、读取本地文件,并且发送
                inChannel = FileChannel.open(Paths.get("d:\a.jpg"), StandardOpenOption.READ);
                
                while(inChannel.read(buf) != -1){
                    buf.flip();
                    socketChannel.write(buf);
                    buf.clear();
                }
                
                //接收服务端的反馈
                int len = 0;
                while((len = socketChannel.read(buf)) != -1){
                    buf.flip();
                    System.out.println(new String(buf.array(),0,len));
                    buf.clear();
                }
                
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                    try {
                        socketChannel.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    try {
                        inChannel.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
            }
        }
        

    此时运行服务端和客户端

    会发现处于阻塞状态

    并无相关的信息进行打印

    此时的解决方案:客户端告诉服务端我已经发送数据完成:

    在客户端中进行添加代码:

                //3、读取本地文件,并且发送
                inChannel = FileChannel.open(Paths.get("d:\a.jpg"), StandardOpenOption.READ);
                
                while(inChannel.read(buf) != -1){
                    buf.flip();
                    socketChannel.write(buf);
                    buf.clear();
                }
                //告诉服务端数据已发送
                socketChannel.shutdownOutput();

     此时是可以接收到服务端的反馈信息

    非阻塞式

    服务端:

    //服务端
        @Test
        public void server() throws IOException{
            //1、获取通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open();
            
            //2、切换非阻塞模式
            ssChannel.configureBlocking(false);
            
            //3、绑定连接
            ssChannel.bind(new InetSocketAddress(8081));
            
            //4、获取选择器
            Selector selector = Selector.open();
            
            //5、将通道注册到选择器上,指定监听事件
            //SelectionKey.OP_ACCEPT:接收
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
            
            //6、轮询式的获取选择器上已经准备就绪的事件
            while(selector.select() > 0){
                //7、获取当前选择器中所有注册的“悬着键”
                Iterator<SelectionKey> it =   selector.selectedKeys().iterator();
                
                //8、迭代获取
                while(it.hasNext()){
                    //9、获取准备就绪的事件
                    SelectionKey sk = it.next();
                    
                    //10、判断是什么事件准备就绪
                    if(sk.isAcceptable()){
                        //接收就绪就获取客户端的连接
                        SocketChannel sChannel = ssChannel.accept();
                        //切换非阻塞模式
                        sChannel.configureBlocking(false);
                        //将该通道注册到选择器上
                        sChannel.register(selector, SelectionKey.OP_READ);
                    }else if(sk.isReadable()){
                        //判断是都是读就绪状态
                        SocketChannel sChannel = (SocketChannel) sk.channel();
                        //读取数据
                        ByteBuffer buf = ByteBuffer.allocate(1024);
                        int len = 0;
                        while((len = sChannel.read(buf)) > 0){
                            buf.flip();
                            System.out.println(new String(buf.array(),0,len));
                            buf.clear();
                        }
                    }
                }
                //取消选择键  SelectionKey sk = it.next();
                it.remove();
            }
        }

    客户端:

    //客户端
        @Test
        public void client() throws IOException{
            //1、获取通道
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8081));
            
            //2、切换非阻塞模式
            sChannel.configureBlocking(false);
            
            //3、指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
            
            //4、发送数据给服务端
            buf.put(new Date().toString().getBytes());
            
            buf.flip();
            
            sChannel.write(buf);
            buf.clear();
            
            //5、关闭通道
            sChannel.close();
            
            
        }

    先运行服务端在运行客户端:

    选择器

    选择器的应用

    SelectionKey

    Selector的常用方法

    SocketChannel

  • 相关阅读:
    iOS SQLite函数总结
    转-NSUserDefaults 简介,使用 NSUserDefaults 存储自定义对象
    NSUserDefaults保存用户名和密码
    iOS 集成银联支付(绕过文档的坑,快速集成)-转
    iOS微信支付
    iOS生成PDF的关键代码-备忘
    python中的urlencode和urldecode
    python中%r和%s的区别
    ubuntu16.04安装wps
    二进制、八进制、十进制、十六进制之间的转换
  • 原文地址:https://www.cnblogs.com/Mrchengs/p/10836011.html
Copyright © 2020-2023  润新知