简述NIO,BIO的区别

大兔子大兔子 提交于 2020-03-01 20:16:37

BIO就是我们传统意义上的Inputstream,Outputstream。这个东西是同步阻塞的,效率不高,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO 随着分布式的如火如荼,为了提高访问效率,引进了NIO,同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
同时这个东西读取或者写出数据的时候不是直接操作channel的,他是操作buffer内存的。这个东西写起来很麻烦,从而诞生了Netty来封装NIO,像一些市面上牛逼的中间件用的都是这东西。比如zk.dubbo等等。。。。
好了说了这么多上代码吧,这东西到底是咋玩的呢?
////////BIO代码服务端和客户端
public class BIOClient {

public static void main(String[] args) throws UnknownHostException, IOException {

	//要和谁进行通信,服务器IP、服务器的端口
	//一台机器的端口号是有限
	Socket client = new Socket("localhost", 8080);

	//输出 O  write();
	//不管是客户端还是服务端,都有可能write和read

	OutputStream os = client.getOutputStream();

	//生成一个随机的ID
	String name = UUID.randomUUID().toString();

	System.out.println("客户端发送数据:" + name);
	//传说中的101011010
	os.write(name.getBytes());
	os.close();
	client.close();

	
}

public class BIOServer {

//服务端网络IO模型的封装对象
ServerSocket server;
//服务器
public BIOServer(int port){
	try {
		//Tomcat 默认端口8080
		//只要是Java写的都这么玩,3306
		//Redis  6379
		//Zookeeper  2181
		server = new ServerSocket(port);
		System.out.println("BIO服务已启动,监听端口是:" + port);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

/**
 * 开始监听,并处理逻辑
 * @throws IOException
 */
public void listen() throws IOException{
	//循环监听
	while(true){
		//等待客户端连接,阻塞方法
		//Socket数据发送者在服务端的引用
		Socket client = server.accept();
		System.out.println(client.getPort());

		//对方法数据给我了,读 Input
		InputStream is = client.getInputStream();

		//网络客户端把数据发送到网卡,机器所得到的数据读到了JVM内中
		byte [] buff = new byte[1024];
		int len = is.read(buff);
		if(len > 0){
			String msg = new String(buff,0,len);
			System.out.println("收到" + msg);
		}
	}
}


public static void main(String[] args) throws IOException {
	new BIOServer(8080).listen();
}

////////////////////////////////////NIO代码演示 客户端可以上BIO的客户端拉起来直接跑
public class NIOServerDemo {

private int port = 8080;

//准备两个东西
//轮询器 Selector
private Selector selector;
//缓冲区 Buffer 
private ByteBuffer buffer = ByteBuffer.allocate(1024);

//初始化完毕
public NIOServerDemo(int port){
    //初始化Selector
    try {
        this.port = port;
        ServerSocketChannel server = ServerSocketChannel.open();
        //我得告诉地址
        //IP/Port
        server.bind(new InetSocketAddress(this.port));
        //BIO 升级版本 NIO,为了兼容BIO,NIO模型默认是采用阻塞式
        server.configureBlocking(false);

        //Selector准备干活
        selector = Selector.open();

        //注册连接事件到selector
        server.register(selector, SelectionKey.OP_ACCEPT);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void listen(){
    System.out.println("listen on " + this.port + ".");
    try {
        //轮询主线程

        while (true){
            selector.select();
            //每次都拿到所有的号子
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iter = keys.iterator();
            //不断地迭代,就叫轮询
            //同步体现在这里,因为每次只能拿一个key,每次只能处理一种状态
            while (iter.hasNext()){
                SelectionKey key = iter.next();
                iter.remove();
                //每一个key代表一种状态
                //没一个号对应一个业务
                //数据就绪、数据可读、数据可写 等等等等
                process(key);
            }
            
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

//每一次轮询就是调用一次process方法,而每一次调用,只能干一件事
//在同一时间点,只能干一件事
private void process(SelectionKey key) throws IOException {
    //针对于每一种状态给一个反应
    if(key.isAcceptable()){
        ServerSocketChannel server = (ServerSocketChannel)key.channel();
        //这个方法体现非阻塞,不管你数据有没有准备好
        //你给我一个状态和反馈
        SocketChannel channel = server.accept();
        //一定一定要记得设置为非阻塞
        channel.configureBlocking(false);
        //当数据准备就绪的时候,将状态改为可读
        key = channel.register(selector,SelectionKey.OP_READ);
    }
    else if(key.isReadable()){
        //key.channel 从多路复用器中拿到客户端的引用
        SocketChannel channel = (SocketChannel)key.channel();
        int len = channel.read(buffer);
        if(len > 0){
            buffer.flip();
            String content = new String(buffer.array(),0,len);
            key = channel.register(selector,SelectionKey.OP_WRITE);
            //在key上携带一个附件,一会再写出去
            key.attach(content);
            System.out.println("读取内容:" + content);
        }
    }
    else if(key.isWritable()){
        SocketChannel channel = (SocketChannel)key.channel();

        String content = (String)key.attachment();
        channel.write(ByteBuffer.wrap(("输出:" + content).getBytes()));

        channel.close();
    }
}

public static void main(String[] args) {
    new NIOServerDemo(8080).listen();
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!