JAVA基础类-NIO基础统归

NIO主要三个部分:

  1. Channels   通道
  2. Buffers  缓冲区
  3. Selectors 选择器

方法简介:

与IO不同的是在NIO中都是基于一个Channel开始。类似流,可以从channel中写到channel中,也可以反写

 Channels(覆盖UDP、TCP、文件IO):
  1. FileChannel                ---文件io
  2. DatagramChannel     ---- udp
  3. SocketChannel         ----- 网络IO
  4. ServerSocketChannel    ---- 监听链接

 :  int byteRead =channel.read(buf)      ---这里特别注明一个transferTo()方法,会比一般的缓冲流复制文件快,跳转相关测试

 : int bytesWritten =channel.write(buf);

如配置Channel为阻塞或者非阻塞模式,以及如何注册到Selector,移步下方Selectors

Buffer(byte,char,short,int,long,float,double)
  1. ByteBuffer
  2. CharBuffer
  3. DoubleBuffer
  4. FloatBuffer
  5. IntBuffer
  6. LongBuffer
  7. ShortBuffer

其三个属性:

capacity:

     含义与模式无关;Buffer的一个固定的大小值;Buffer满了需要将其清空才能再写;
          ByteBuffer.allocate(48);该buffer的capacity为48byte
          CharBuffer.allocate(1024);该buffer的capacity为1024个char

position:

    含义取决于Buffer处在读模式还是写模式(初始值为0,写或者读操作的当前位置)
写数据时,初始的position值为0;其值最大可为capacity-1
将Buffer从写模式切换到读模式,position会被重置为0

 

limit

    含义取决于Buffer处在读模式还是写模式(写limit=capacity;读limit等于最多可以读取到的数据)
写模式下,limit等于Buffer的capacity




flip()方法
将Buffer从写模式切换到读模式,取决于当前状态
调用flip()方法会将position设回0,并将limit设置成之前position的值。


rewind()方法

将position设回0,所以你可以重读Buffer中的所有数据
limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)


get() 方法,put()方法

一般是调用返回第一个字节


mark()方法,reset()方法

可以标记Buffer中的一个特定position。之后可以通过调用reset()恢复到Buffer.mark()标记时的position

position()、limit() 相当于读取的开始和结束字节下标,如果limit没有限制,postion多少字节,就空出多少字节


clear()方法

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入,清空整个缓冲区,position归0,limit设回capacity(自己设置的容量)


compact()方法

只会清除已经读过的数据;任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
将position设到最后一个未读元素正后面,limit被设置成 capacity的值



Selector

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。 要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等


创建:

    Selector selector = Selector.open(); 


注册:

   channel.configureBlocking(false); //boolean为设置是否阻塞   tip: 阻塞即等待IO处理完后才返回

       与Selector一起使用时,Channel必须处于非阻塞模式这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式(而套接字通道都可以)


   SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

    第二个参数表明Selector监听Channel时对什么事件感兴趣

      ①SelectionKey.OP_CONNECT   连接

     ②SelectionKey.OP_ACCEPT      接收

     ③SelectionKey.OP_READ         读取

      ④SelectionKey.OP_WRITE       循环


  SelectionKey 包含了interest集合 、ready集合 、Channel 、Selector 、附加的对象(可选)

    int interestSet = key.interestOps();可以进行类似interestSet & SelectionKey.OP_CONNECT的判断
         使用:

            select():阻塞到至少有一个通道在你注册的事件上就绪了
          selectNow():不会阻塞,不管什么通道就绪都立刻返回
          selectedKeys():访问“已选择键集(selected key set)”中的就绪通道
          close():使用完selector需要用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效




应用实例:

  、buffer结合channel 基本操作      BIO

  tip:

CharSet
用于构建String 和ByteBuffer,以及编码的的一个转换类

构建:
Charset charSet = Charset.forName("gbk");//window系统默认txt文本是gbk

charSet.decode(butBf)  ,  用于byteBuffer to String

charSet.encode("测试下")  用于String to byteBuffer

 


/**
	* @Title: Fastcopy
	* @Description: 快速复制
	* @throws FileNotFoundException
	* @throws IOException    设定文件 
	* @return void    返回类型 
	* @throws
	*/
	private static void Fastcopy() throws FileNotFoundException, IOException {
		//打开输入流
		FileInputStream fis = new FileInputStream("E:/家园游戏.txt");
		//打开输出流
		FileOutputStream fos = new FileOutputStream("C:/Users/Administrator/Desktop/直播平台数据/game.txt");
		try{
		//获取通道
		FileChannel readchannel = fis.getChannel();
		FileChannel writechannel = fos.getChannel();
		readchannel.transferTo(0, readchannel.size(), writechannel);
		}catch(IOException e){
			e.printStackTrace();
		}finally {
			fis.close();
			fos.close();
		}
	}

	/**
	* @Title: readFile
	* @Description: 读取文件
	* @throws FileNotFoundException
	* @throws IOException    设定文件 
	* @return void    返回类型 
	* @throws
	*/
	private static void readFile() throws FileNotFoundException, IOException {
		//打开输入流
		FileInputStream fis = new FileInputStream("E:/家园游戏.txt");
		try{
		//获取通道
		FileChannel channel = fis.getChannel();
		//设置容量
		ByteBuffer allocate = ByteBuffer.allocate(1024);
		channel.read(allocate);
		//window系统的默认编码
		Charset charset = Charset.forName("gbk");
		//修改为读模式
		allocate.flip();
		System.out.println(charset.decode(allocate));
		}catch(IOException e){
			e.printStackTrace();
		}finally {
			fis.close();
		}
	}



二、结合selector基本操作网络IO(套接字) NIO

 

对某一端口进行监控

	public static void main(String[] args) throws Exception {
		ServerSocketChannel serverSocketChannel  = ServerSocketChannel.open();
		//设置为非阻塞
		serverSocketChannel.configureBlocking(false);
		//绑定IP和接口
		serverSocketChannel.socket().bind(new InetSocketAddress("192.168.1.212", 8080));
		Selector selector = Selector.open();
		// 注册感兴趣事件到selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //判断是否存在通道
        while(selector.select()!=0){
        	Iterator iterator = selector.selectedKeys().iterator();
        	while (iterator.hasNext()) {
        		//循环选中事件
				SelectionKey selecttionKey = iterator.next();
				//删除已经处理的
				iterator.remove();
                if (selecttionKey.isAcceptable()) {
                    // 返回注册该事件时的channel ,即SelectableChannel
                    ServerSocketChannel channel = (ServerSocketChannel) selecttionKey.channel();
                    // 有连接事件来了, 可以处理接收请求了,注意如果不进行accept,select.select()一直能轮询到东西
                    // 接收后返回了个socketchannel,开始配置
                    SocketChannel socketChannel = channel.accept();
                    // 也配置成非阻塞处理
                    socketChannel.configureBlocking(false);
                    // 复用同一个selector上注册感兴趣的事件,并注册感兴趣的可读事件
                    socketChannel.register(selector, selecttionKey.OP_READ);
                }
             // 如果来可以可读事件
                if (selecttionKey.isReadable()) {
                    // 返回注册该事件时的channel ,即实现了SelectableChannel的
                    SocketChannel socketChannel = (SocketChannel) selecttionKey.channel();
                    // 后面就都是通过byteBuffer和channel来读操作了
                    ByteBuffer byteBf = ByteBuffer.allocate(1024);
                    socketChannel.read(byteBf);
                    Charset charset = Charset.forName("utf-8");
                    byteBf.flip();
                    System.out.println("clinet :" + charset.decode(byteBf));
                    // socket是双通道,故也可以直接返回东西了
                    socketChannel.write(charset.encode("test only"));
                    socketChannel.close();
                }
			}
        }
	}


什么时候用IO,什么时候用NIO呢?

大量链接涌入的时候,传的数据比较少,然后处理时间比较长,的时候适合NIO(偏向IO密集型)

如果传入的链接比较少,然后传输数据量大,比如文件上传之类,适合BIO



NIO : 用一个thread(selector)做服务接收,和链接的维持

IO  : 用一个thread做服务接收,其它每个链接都用一条线程保持



压力测试 ------------跳转ab压力测试指引

你可能感兴趣的:(基础)