Java NIO 学习总结(二)

Java NIO 学习总结(二)
主要内容:
    1.阻塞与非阻塞 1.阻塞与非阻塞
    2.DatagramChannel

一、阻塞与非阻塞
    阻塞:客户端请求服务端时,读写请求不能及时处理时,服务端处理线程与客户端请求线程就会处于占用(等待)的阻塞状态;
    非阻塞:NIO提出的选择器(类似于中间异步)
        1.先把客户端连Server的Channel注册到选择器上,选择器在Server与Client之间,声明在服务端
        2.通道上存在IO状态(包括:读、写、连接、接收数据)可以供选择器监控
        3.当通道的某个状态就绪时,选择器才会将客户端请求分配到Server的一个或者多个线程上
    示例:
    1.阻塞式NIO(new IO)(无选择器)实现文件传输

package com.jupiter.test;

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * @author Jupiter
 * @date 2019/3/6-22:05
 * @description 测试NIO的阻塞方式完成数据文件的传输
 */
public class TestBlockingNIOChannel {

    /**
     * 1.使用NIO完成网络通信的三个核心
     *  |--通道:负责连接
     *  |--缓冲区:负责传输数据
     *  |--选择器:是SelectableChannel的多路复用器,用于监控SelectableChannel的状态
     */

    @Test
    public void client() throws IOException {
        //1.建立连接服务端的通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898 ));
        //获取本地文件
        FileChannel inChannel = FileChannel.open(Paths.get("o:/1.txt"), StandardOpenOption.READ);
        //inChannel.transferTo(0, inChannel.size(), sChannel); //利用直接缓冲区传输文件,可以实现,这样下面的步骤2可以省略
        //2.建立负责数据传输的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(inChannel.read(buffer) != -1){
            buffer.flip();
            sChannel.write(buffer);
            buffer.clear();
        }
        //3.关闭通道
        inChannel.close();
        sChannel.close();
    }

    @Test
    public void server() throws IOException {
        //1.建立服务端通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        //2.绑定链接
        ssChannel.bind(new InetSocketAddress(9898));

        //2.建立接收客户端数据的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        FileChannel outChannel = FileChannel.open(Paths.get("2.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        //3.接收客户端的socketChannel,如果客户端一直没有请求,那么该线程一直处于阻塞状态!
        SocketChannel acceptChannel = ssChannel.accept();

        while(acceptChannel.read(buffer)!=-1){
            buffer.flip();
            outChannel.write(buffer);
            buffer.clear();
        }
        acceptChannel.close();
        outChannel.close();
        ssChannel.close();
    }
}

    2.非阻塞式NIO实现聊天室

package com.juwenzhe.nio.test;
import org.junit.Test;
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.time.LocalTime;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @author Jupiter
 * @date 2019/3/6-22:49
 * @description 用非阻塞方式实现聊天室
 */
public class TestNonBlockingChannel {

    @Test
    public void client() throws IOException {
        //1.创建客户端连接服务端的通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        sChannel.configureBlocking(false); //设置通道为非阻塞模式

        //2.创建传输数据的buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            //可以判断输入的字符长度
            String src = sc.next();
            buffer.put((LocalTime.now().toString()+"\n"+src).getBytes());
            buffer.flip();
            sChannel.write(buffer);
            buffer.clear();//不写的话,缓冲区一直被上一次写入占着,相当于flush,不能进行下次写入
        }
        sChannel.close();
        sc.close();
    }

    @Test
    public void server() throws IOException {

        //1.建立服务端连接通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        ssChannel.bind(new InetSocketAddress(9898));
        //2.切换非阻塞模式
        ssChannel.configureBlocking(false);
        //3.创建选择器
        Selector selector = Selector.open();
        //4.注册监听服务端ssChannel的接收客户端Socket事件
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        //一直轮询监听客户端连接服务端的操作,selector.select()会阻塞,原理->参考文献
        while(selector.select()>0){
            //获取每次轮询得到的选择键(监听到的事件)
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()){

                SelectionKey next = it.next();
                //判断具体是什么事件准备就绪
                if (next.isAcceptable()){
                    //如果是“准备接收”事件准备就绪
                    //建立接收客户端请求的Channel
                    SocketChannel sChannel = ssChannel.accept();
                    //切换到非阻塞模式
                    sChannel.configureBlocking(false);
                    //接下来将该通道注册到选择器上,监听socketChannel的读就绪状态
                    sChannel.register(selector,SelectionKey.OP_READ);
                }else if(next.isReadable()){
                    //建立该选择键对应的通道与缓冲区
                    SocketChannel sChannel = (SocketChannel) next.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    while(sChannel.read(buffer)>0){
                        buffer.flip();
                        System.out.println(new String(buffer.array(), 0, buffer.limit()));
                        buffer.clear();
                    }
                }
                //清除单次轮询监听到的客户端选择键
                it.remove();
            }
        }
    }
}

二、DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。

package com.juwenzhe.nio.test;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.time.LocalTime;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

/**
 * @author Jupiter
 * @devDate 2019年3月12日
 * @description 使用基于UDP的非阻塞NIO网络通信聊天室
 */
public class TestNonBolckingNIO {

    @Test
    public void client() throws Exception{
        //1.创建通道
        DatagramChannel dgc = DatagramChannel.open();
        
        // 通道开启非阻塞模式
        dgc.configureBlocking(false);
        //2.创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            //向缓冲区写入数据
            String src = sc.nextLine();
            buffer.put((LocalTime.now()+"\n"+src).getBytes());
            //切换缓冲区为读模式
            buffer.flip();
            //通过UDP管道写出数据
            dgc.send(buffer, new InetSocketAddress("127.0.0.1", 9898));//与TCP的传输方式不同,TCP:连接的write,sChannel.write(buffer);
            buffer.clear();
        }
        dgc.close();
        sc.close();
    }
    
    @Test
    public void server() throws Exception{
        //1.创建通道
        DatagramChannel dgc = DatagramChannel.open();
        // 服务端绑定端口号
        dgc.bind(new InetSocketAddress(9898));
        // 管道切换为非阻塞模式
        dgc.configureBlocking(false);
        //2.创建选择器
        Selector selector = Selector.open();
        //3.服务端管道注册监听动作到选择器
        dgc.register(selector, SelectionKey.OP_READ);
        while(selector.select()>0){
            //4.获取监听到的客户端动作
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while(iter.hasNext()){
                //5.现只判断选择器上的监听到的动作,UDP面向无连接,与TCP的传输方式不同,TCP:连接先accept建立连接,再继续监听其他动作
                SelectionKey sk = iter.next();
                if(sk.isReadable()){
                    //6.如果有客户端传来数据,直接打印数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    //向buffer中传入数据
                    dgc.receive(buffer);
                    buffer.flip();
                    System.out.println(new String(buffer.array(), 0, buffer.limit()));
                    buffer.clear();
                }
            }
            iter.remove();
        }
    }
}

参考文献
☆ 深入浅出NIO之Selector实现原理

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值