NIO原理详解(一)

了解一下简单的概念

阻塞:我们需要等待数据准备好的时候才可以进行其他的操作
非阻塞:我们不需要等待数据准备好之后,我们也可以进行其他的操作
我的的NIO是同步非阻塞的,而我们的BIO是同步阻塞的

了解一下传统的BIO

在这里插入图片描述
简要的文字说明:在BIO中,client与server端之间是面向流的,通过留来进行传输的,每个client连接到server端时都会开启一条通道,都有一个Thread来处理,当client断开是,通断换删除掉
这样导致的问题:1.阻塞问题,2.开销问题,3.多线程接受多个请请求

了解一下Java 1.5后出现的NIO

在这里插入图片描述
简要的文字说明:在NIO中,client与server的通信是面向留的,而且他是IO多路复用的,他是非阻塞的,不会让你阻塞在channel上面,只要你有消息过来,他都会只通过一个死循环线程,不断的轮询,然后把他塞到buffer缓冲区里面,不需要等待数据准备完了,才进行其他的操作。单线程接受多个请求

传统BIO的代码实现

client端:

package com.bio;

import java.io.OutputStream;
import java.net.Socket;

/**

  • 传统的Bio的client端

  • @author michael

  • @date 2019-03-28 08:52
    */
    public class BioClient {

    /**

    • main
    • @param args
      */
      public static void main(String[] args) throws Exception {
      BioClient bioClient = new BioClient();
      bioClient.setBioClient();
      }

    private void setBioClient() throws Exception {
    //创建一个sooket相当于建好一条乡村通道,需要是才创建
    Socket client = new Socket(“127.0.0.1”, 8089);
    //要往这条通道塞东西,就拿到这条通道的OutputStream
    OutputStream outputStream = client.getOutputStream();
    //我们要输出的内容
    String name = “Michael”;
    //把数据塞到流里面
    outputStream.write(name.getBytes());
    //关闭流
    outputStream.close();
    //关闭socket
    client.close();
    }
    }

server端:

package com.bio;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**

  • 传统BIO-server端

  • @author michael

  • @date 2019-03-28 09:11
    */
    public class BioServer {

    ServerSocket server;

    //服务器
    public BioServer(int port) {
    try {
    //把Socket服务端启动,socket的服务端,new ServerSocket(int port)
    server = new ServerSocket(port);
    System.out.println(“BIO服务已启动,监听端口是:” + port);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    /**

    • 监听
    • @throws Exception
      */
      public void listener() throws Exception {
      //使用死循环,不断的获取client的数据
      while (true) {
      //由server的accpt方法获取client,该方法是阻塞的,由server获取到乡村通道
      Socket client = server.accept();
      //对于server而言,我们要从乡村通道里面获取到需要塞进来的东西
      InputStream inputStream = client.getInputStream();
      //缓冲区,数组而已
      byte[] buff = new byte[1024];
      //把东西放到缓冲区里面,然后输出
      int len = inputStream.read(buff);
      //判断
      if (len > 0) {
      //缓冲转换字符串
      String name = new String(buff, 0, len);
      System.out.println(name);
      }
      }
      }

    /**

    • 主函数
    • @param args
      */
      public static void main(String[] args) throws Exception {
      new BioServer(8089).listener();
      }
      }

总结:

client
1.在client连接server端的时候,我们首先需要建设打开一条通道,new Socket(“IP”,port);
2.然后我们需要从通道获取输出流,才能把数据输出到通道,client.getOutputStream();
3.接着我们把需要的内容写到流里面,outputStream.write(“xxxxx”)
4.最后我们需要关闭流,关闭通道
思想,客户端创建通道,从通道里回去流,流里放数据
server
1.在server端,首先我们要根据port创建serverSocket;
2.接着我们要死循环,不断的获取连接到我们的通道Socket client = serverSocket.accpet();
3.接着获取我们读取的输入流InpustStream =client.getInpustStream()
4. 把东西放到缓冲区里面,然后输出int len = inputStream.read(buff);
思想:根据port创建客户端,循环,不断获取连接到我们身上的client(通道),获取流,然后输出
缺点是:他需要多个线程才能接受多个客户端,且他的通道没办法复用,用完之后他会回收掉

NIO的代码实现

client端:

package com.nio;

import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

/**

  • NIO客户端

  • @author michael

  • @date 2019-03-28 10:06
    */
    public class NIOClient {

    /**

    • NIO传输的高速公路的端口
      */
      private final InetSocketAddress inetSocketAddress = new InetSocketAddress(“localhost”, 8089);

    /**

    • NIO的客户端,对比于传统的而言转换为SocketChannel
      */
      private SocketChannel client = null;

    /**

    • 控制字符串格式
      */
      private Charset charset = Charset.forName(“UTF-8”);

    /**

    • 构造函数
      */
      public NIOClient() throws Exception {
      //不管什么,先建一条高速公路先
      client = SocketChannel.open(inetSocketAddress);
      }

    /**

    • 写数据
    • @param name
    • @throws Exception
      */
      public void write(String name) throws Exception {
      //输出数据
      client.write(charset.encode(name));
      }

    /**

    • main函数
    • @param args
    • @throws Exception
      */
      public static void main(String[] args) throws Exception {
      new NIOClient().write(“Michael”);
      }
      }

server端:

package com.nio;

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.util.Iterator;
import java.util.Set;

/**

  • NIO server端

  • @author michael

  • @date 2019-03-28 11:17
    */
    public class NIOServer {

    private int port = 8089;

    private InetSocketAddress address = null;

    private Selector selector = null;

    /**

    • 构造函数
      */
      public NIOServer() throws Exception {
      //设置IP,端口相关的属性
      address = new InetSocketAddress(this.port);
      //不管做什么,都要创建高速通道
      ServerSocketChannel server = ServerSocketChannel.open();
      server.bind(address);
      server.configureBlocking(false);
      //大管家开始工作,开门营业
      selector = Selector.open();
      //Option的简称
      server.register(selector, SelectionKey.OP_ACCEPT);
      System.out.println(“服务器准备就绪,监听端口是:” + this.port);
      }

    public void listen() {
    try {
    //轮询
    while (true) {
    //有多少个人在服务大厅排队
    int wait = this.selector.select();
    if (wait == 0) {
    continue;
    }
    Set keys = this.selector.selectedKeys();
    Iterator i = keys.iterator();
    while (i.hasNext()) {
    SelectionKey key = i.next();
    //
    process(key);
    i.remove();
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    public void process(SelectionKey key) throws Exception {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    if (key.isAcceptable()) {
    ServerSocketChannel server = (ServerSocketChannel) key.channel();
    SocketChannel client = server.accept();
    client.configureBlocking(false);
    client.register(selector, SelectionKey.OP_READ);
    } else if (key.isReadable()) {
    SocketChannel client = (SocketChannel) key.channel();
    int len = client.read(buffer);
    if (len > 0) {
    buffer.flip();
    String content = new String(buffer.array(), 0, len);
    System.out.println(content);
    client.register(selector, SelectionKey.OP_WRITE);
    }
    buffer.clear();
    } else if (key.isWritable()) {
    SocketChannel client = (SocketChannel) key.channel();
    client.write(buffer.wrap(“Hello Wold”.getBytes()));
    client.close();
    }
    }

    public static void main(String[] args) throws Exception {
    new NIOServer().listen();
    }
    }
    client:
    1.不管什么,先建一条高速公路先client =SocketChannel.open(inetSocketAddress);
    2.往这条通道输出数据 client.write(charset.encode(name));

server端:
1.首先,我们都要先建高速公路通道, ServerSocketChannel server = ServerSocketChannel.open();,需要绑定地址,指定最大连接数:server.bind(address(InetSocketAddress));设置为非阻塞的:server.configureBlocking(false);
2.Selector营业大厅开门营业了,到的client,他都会把他接进来
开门营业 selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
3.接着我们死循环,不断的看有没有人在排号等待,如果有的话,就要把排号的那一批人叫过来
if (wait == 0) {
continue;
}
Set keys = this.selector.selectedKeys();
接着遍历他们每一个人的排号码,根据排号码,每一个人就来到服务台,读到他们的信息
SelectionKey key = i.next();
process(key);
SocketChannel client = (SocketChannel) key.channel();
int len = client.read(buffer);
if (len > 0) {
buffer.flip();
String content = new String(buffer.array(), 0, len);
System.out.println(content);
client.register(selector, SelectionKey.OP_WRITE);
}
buffer.clear();
4.办完业务,就把他remove掉

猜你喜欢

转载自blog.csdn.net/sinat_30594513/article/details/88858383
今日推荐