NIO高级编程与Netty入门
一、NIO同步阻塞IO&NIO非阻塞IO
IO(BIO)与NIO的区别:其本质就是阻塞IO和非阻塞IO的区别。
我们所用的IO都是同步阻塞式IO也叫BIO
阻塞:应用在获取网络数据的时候,如果网络传输速率很慢,就会一直等待直到
传输完成为止。
非阻塞:应用程序可以直接获取已经准备好的数据。无需等待。
IO:为同步阻塞
NIO:为同步非阻塞,NIO并没有实现异步,在jdk1.7之后支持异步非阻塞通讯。
NIO2.0(AIO)
BIO(IO):同步阻塞式IO
NIO:同步非阻塞式IO
AIO:异步非阻塞式IO
解决问题:同步阻塞IO的办法?
解决办法:将阻塞的那下面代码放到多线程里面。
1、使用多线程,也称为伪异步阻塞
2、使用线程池
伪异步形式缺点:
1、没有真正解决阻塞IO核心。
2、创建太多线程,消耗内存。
NIO:同步非阻塞、用于网络相关的。
NIO使用selected选择器来实现同步非阻塞的。
非阻塞IO:数据准备就绪之后,由选择器通知给服务器,
数据在准备之前服务器无需等待。
阻塞IO:就是服务器会一直等待客户端传来的数据。
二、NIO非阻塞 客户端与服务端 代码
package com.leeue.nio;
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.Date;
import java.util.Iterator;
/**
*
* @classDesc: 功能描述:(实现nio客户端与服务端 写的是TCP协议的)
* @author:<a href="[email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年7月30日 下午3:57:14
*/
class NioCliet {
public static void main(String[] args) throws IOException {
System.out.println("客户端已经被启动了...");
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
sChannel.configureBlocking(false);
ByteBuffer buff = ByteBuffer.allocate(1024);
buff.put(new Date().toString().getBytes());
buff.flip();
sChannel.write(buff);
buff.clear();
sChannel.close();
}
}
class NIOServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器端已被启动...");
ServerSocketChannel sChannel = ServerSocketChannel.open();
sChannel.configureBlocking(false);
sChannel.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
sChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey sk = iterator.next();
if (sk.isAcceptable()) {
SocketChannel socketChannel = sChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
SocketChannel socketChannel = (SocketChannel) sk.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
while((len=socketChannel.read(byteBuffer))>0) {
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
}
iterator.remove();
}
}
}
}
public class NIOCSDemo01 {
}
选择KEY
1、SelectionKey.OP_CONNECT
2、SelectionKey.OP_ACCEPT
3、SelectionKey.OP_READ
4、SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey类的源码中我们可以看到如下的4中属性,
四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接.
三、Netty入门
什么是Netty?
netty是基于Java NIO 类库实现的异步通讯框架,它的特点:异步非阻塞、基于事件
驱动,性能高,高可靠性和高可定制性。
netty应用场景?
1、分布式开源框架中。
dubbo zookeeper rocketMQ底层的rpc通讯就是使用netty
2.游戏开发中底层使用netty通讯。
为什么使用netty?
解决传统的NIO非阻塞代码的bug问题,进行一些封装,因为传统的,NIO代码比较
复杂。netty使用的事件驱动。
netty:是一个异步通讯框架,底层对NIO进行封装了,NIO框架也是一个通讯框架。
使用netty写的服务端程序代码,并证明netty不是阻塞一直等待的
package com.leeue.netty;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
class ServerHanlder extends SimpleChannelHandler{
/**
* 通道关闭的时候触发
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelClosed(ctx, e);
System.out.println("channelClosed");
}
/**
* 必须要建立连接,然后关闭通道的时候才会触发
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelDisconnected(ctx, e);
System.out.println("channelDisconnected");
}
/**
* 接受出现异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
super.exceptionCaught(ctx, e);
System.out.println("exceptionCaught");
}
/**
* 接受客户端数据
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
super.messageReceived(ctx, e);
System.out.println("messageReceived");
System.out.println("服务器端获取客户端发来的参数:"+e.getMessage());
}
}
public class NettyServer {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService wook = Executors.newCachedThreadPool();
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, wook));
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encode",new StringEncoder());
pipeline.addLast("serverHanlder", new ServerHanlder());
return pipeline;
}
});;
serverBootstrap.bind(new InetSocketAddress(8080));
System.out.println("服务器端已经被启动.......");
while(true) {
try {
Thread.sleep(1000);
System.out.println("netty没有阻塞嘻嘻");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用netty实现的客户端
package com.leeue.netty;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
*
* @classDesc: 功能描述:(使用netty实现的客户端)
* @author:<a href="[email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年7月30日 下午5:25:14
*/
class ClientHanlder extends SimpleChannelHandler{
/**
* 通道关闭的时候触发
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelClosed(ctx, e);
System.out.println("channelClosed");
}
/**
* 必须要建立连接,然后关闭通道的时候才会触发
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelDisconnected(ctx, e);
System.out.println("channelDisconnected");
}
/**
* 接受出现异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
super.exceptionCaught(ctx, e);
System.out.println("exceptionCaught");
}
/**
* 接受客户端数据
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
super.messageReceived(ctx, e);
System.out.println("messageReceived");
System.out.println("服务器端向客户端回复的内容:"+e.getMessage());
}
}
public class NettyClient {
public static void main(String[] args) {
ClientBootstrap clientBootstrap = new ClientBootstrap();
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService wook = Executors.newCachedThreadPool();
clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, wook));
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encode",new StringEncoder());
pipeline.addLast("clientHanlder", new ClientHanlder());
return pipeline;
}
});
ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
System.out.println("客户端已经被启动.......");
Channel channel = connect.getChannel();
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("请输入内容");
channel.write(scanner.nextLine());
}
}
}
非阻塞IO:服务器端只有在拿到客户端传来的数据执行客户端的,服务器端不会等待。
阻塞IO:服务期端会一直等待客户端传数据。服务器端不能干其他事
同步非阻塞:同步表示在管道里面获取的时候是单线程获取值
异步非阻塞:异步是从多个单独线程去获取值
IO与NIO区别?
NIO:有通道、非阻塞、缓冲区 NIO是面向缓冲区的,还有选择器
非阻塞IO与阻塞IO?
netty应用场景?