java NIO原理和代码实践

一,先说java IO 

     1,线程阻塞:当线程调用write()read()时,线程会被阻塞,直到有一些数据可用于读取或数据被完全写入。

     2,面向流: 我们需要从流中读取一个或多个字节。它使用流来在数据源/槽和java程序之间传输数据。从源进入 Java对象成为“input” , 从Java 对象 写出称为“ouput” .

 

二,同步非阻塞IO:当内核数据没有准备好的情况下,并不会一直阻塞等待,而是立即返回。随后多次发起系统调用,轮询过程。

            优点: 不会等待阻塞,失败立即返回。

            缺点: 多次发起调用,轮询过程消耗资源。

 

三,多路复用IO:select/epoll系统调用,单个线程不断的轮询select/epoll系统调用所负责的成百上千的socket连接,当某个或者某些socket网络连接有数据到达了,就返回这些可           以读写的连接。 在 Linux 下,实现高并发网络编程时都是以 IO 复用模型模式为主。这是同步非阻塞的升级。

           优点:之前是一个线程对应一个连接。现在是一个线程对应多个连接。  适合高并发场景;系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。

           缺点:需要不断的进行select/epoll轮询,查找出可以进行IO操作的连接;select/epoll系统调用,属于同步IO,也是阻塞IO。也就是说这个读写过程是阻塞的。

 

二,java NIO 原理  

     就是多路复用原理。

      

三 , NIO 简单例子

  

package org.example;

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

public class NIOServer {

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

        Selector selector = Selector.open(); // 开启选择器
        ServerSocketChannel SocketChannel = ServerSocketChannel.open();
        InetSocketAddress addr = new InetSocketAddress("localhost", 1111);
        SocketChannel.bind(addr);  //服务器端建立channel ,绑定到端口
        SocketChannel.configureBlocking(false);  // 配置channel阻塞模式
        int ops = SocketChannel.validOps();
        SelectionKey selectKy = SocketChannel.register(selector, ops, null);
        // 无限循环,保持服务器运行
        while (true) {
            log("我是服务器,我在等待连接... ");
            selector.select();  // 从很多key中选择 已IO准备好的channel
            Set<SelectionKey> Keys = selector.selectedKeys();  //从选择器中选择“已准备好的通道(对应的key)”
            Iterator<SelectionKey> Iterator = Keys.iterator();
            while (Iterator.hasNext()) {
                SelectionKey myKey = Iterator.next();
                //测试通道是否可以接收socket 连接
                if (myKey.isAcceptable()) {
                    SocketChannel socketChannel = SocketChannel.accept();
                    // 修改通道阻塞模式
                    socketChannel.configureBlocking(false);
                    // 为读操作进行配置
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    log("成功连接到: " + socketChannel.getLocalAddress() + "\n");
                    // 测试通道是否可以读
                } else if (myKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) myKey.channel();
                    ByteBuffer Buffer = ByteBuffer.allocate(256);
                    socketChannel.read(Buffer);
                    String result = new String(Buffer.array()).trim();
                    log("接收到消息: " + result);
                    if (result.equals(".com")) {
                        socketChannel.close();
                        log("\n接收到最后一个companie name,可以关闭连接 ''");
                        log("\n服务器保持运行,可以继续重启客户端建立新连接进行测试。 ");
                    }
                }
                Iterator.remove();
            }
        }
    }

    private static void log(String str) {
        System.out.println(str);
    }
}


public class NIOClient {
    public static void main(String[] args) throws IOException, InterruptedException {

        InetSocketAddress addr = new InetSocketAddress("localhost", 1111);

        //  建立数据报通道,并绑定地址
        SocketChannel socketChannel = SocketChannel.open(addr);  //channel 绑定到端口。

        log("连接到服务器,端口1111...");
        ArrayList<String> companyDetails = new ArrayList<String>();  //构造传输的数据

        companyDetails.add("Facebook");
        companyDetails.add("Twitter");
        companyDetails.add("IBM");
        companyDetails.add("Google");
        companyDetails.add("alibaba.com");
        companyDetails.add(".com");

        for (String companyName : companyDetails) {
            byte[] message = new String(companyName).getBytes();
            ByteBuffer buffer = ByteBuffer.wrap(message);  // 功能:定位 ,容量,
            socketChannel.write(buffer);  //将Buffer中的数据读入通道
            log("发送消息: " + companyName);
            buffer.clear();
            Thread.sleep(2000);
        }
        socketChannel.close();
    }

    private static void log(String str) {
        System.out.println(str);
    }
}

 

运行结果:

 

posted @ 2022-06-12 01:03  gaussen126  阅读(118)  评论(0编辑  收藏  举报