netty学习一 Java IO(BIO、NIO)

几个概念

阻塞与非阻塞

阻塞:往往需要等待缓冲区中数据准备好过后才处理其他事情,否则一直等待。
非阻塞:当我们进程访问数据缓冲区时,如果数据没有准备好则直接返回,不会的等待。数据准备好了,也直接返回。

同步和异步

同步和异步都是基于应用程序和操作系统处理IO事件所采用的方式。比如同步:是应用程序要直接参与IO读写的操作。异步:所有的IO读写交给操作系统去处理,应用程序只需要等待通知。
同步方式在处理IO事件的时候,必须阻塞在某个方法上面等待我们的IO事件完成(阻塞IO事件或者通过轮询IO事件的方式),对于异步来说,所有的IO读写都交给了操作系统。这个时候,我们可以去做其他的事情,并不需要去完成真正的IO操作,当操作完成IO后,会给我们的应用程序一个通知。
同步:阻塞到IO事件,阻塞到read或则write。这个时候我们就完全不能做自己的事情。让读写方法加入到线程里面,然后阻塞线程来实现,对线程的性能开销比较大。

BIO和NIO*

JavaNIO和BIO之间第一个最大的区别是,BIO是面向流的,NIO是面向缓冲区的
在这里插入图片描述
BIO是阻塞的,当一个线程调用read()和write()方法时,该线程会被阻塞,直到数据被读取或完全写入。
NIO是非阻塞的,一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,没有数据可用,就不会获取。直至数据变得可读之前该线程可以做其他事情,而不是一直阻塞等待。
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)

代码实现
BIO服务端

//同步阻塞IO模型
public class BIOServer {

	//服务端网络IO模型的封装对象
	ServerSocket server;
	//服务器
	public BIOServer(int port){
		try {
			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();
	}
	
}

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();

		
	}
	
}

NIO服务端

public class NIOServerDemo {

    private int port = 8080;

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

    //初始化完毕
    public NIOServerDemo(int port){
        //初始化大堂经理,开门营业
        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.open();

            //在门口翻牌子,正在营业
            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();
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值