Netty简介
Netty是一款用于创建高性能网络应用程序的IO框架,它对Java的原生IO进行封装,提供了更加简洁易用的API,支持阻塞和非阻塞,并且推崇将应用逻辑代码和网络连接代码解耦。
Netty核心组件
- Channel
- 回调
- Future
- 事件和ChannelHandler
Channel
通道,NIO中的构造,类似于BIO中的流。BIO中面向流,NIO中面向Channel。Channel表示一个连接,是出站和入站数据的载体,可以对该连接进行读写。
回调
就是一个方法,一个指向 已经被提供给另一个方法 的方法的引用。使得后者可以在适当的时机调用前者。在Netty中,使用回调来处理事件。当一个回调被触发,相关事件会被一个实现了ChannelHandler接口的对象的方法处理。
Future
Netty中实现操作完成时通知应用程序的另一方式。JDK中的Future需要手动查看结果,因此可能会造成轮询或者阻塞,Netty中给了ChannelFuture实现,可以自动通知,用于异步通知。
ChannelFuture中提供了额外方法,使得一个ChannelFuture可以注册一个或多个ChannelFutureListener实例。监听器的回调方法operationComplete()将会在对用的操作完成时被调用。监听器可以判断操作是否成功,若失败可以检查抛出的Throwable。
每个Netty的出站操作都会返回一个ChannelFuture,也就是说不会阻塞,线程可以做别的事情。
事件和ChannelHandler
每个事件可以被分发给对应的ChannelHandler实现类处理。
编写EchoServer
EchoServerHandler的编写
package server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* @ClassName EchoServerHandler
* @Description TODO
* @Author LYcreate
* @Date 2019/2/22 21:41
*/
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 对于每个传入的消息都调用
* @author LYcreate
* @date 2019/10/14 21:22
* @param ctx
* @param msg
* @return void
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in=(ByteBuf) msg;
System.out.println("Server received:"+in.toString(CharsetUtil.UTF_8));
//将收到的消息写给发送者,但不冲刷消息。该操作不会阻塞,直接返回结果,因此此时写数据可能还未完成。
ctx.write(in);
}
/**
* 通知ChannelHandler最后一次对channelRead()调用是当前批量读取中的最后一条消息
* @author LYcreate
* @date 2019/10/14 21:23
* @param ctx
* @return void
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将未决消息冲刷给远程节点,并且关闭该Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
EchoServer引动类的编写
package server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
/**
* @ClassName EchoServer
* @Description TODO
* @Author LYcreate
* @Date 2019/2/22 21:54
*/
public class EchoServer {
private final int port=8080;
public EchoServer(){
}
public static void main(String[] args) throws Exception{
new EchoServer().start();
}
public void start() throws Exception{
final EchoServerHandler serverHandler=new EchoServerHandler();
//NIO传输使用NioEventLoopGroup
EventLoopGroup group=new NioEventLoopGroup();
try{
ServerBootstrap b=new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
//ChannelInitializer会将ChannelHandler添加到Channel对应的ChannelPipeline中
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(serverHandler);
}
});
//绑定服务器,通过sync()方法同步返回
ChannelFuture f=b.bind().sync();
//阻塞等待关闭
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
group.shutdownGracefully().sync();
}
}
}编写EchoClient
编写EchoClientHandler
package client;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
/**
* @ClassName EchoClientHandler
* @Description TODO
* @Author LYcreate
* @Date 2019/2/22 22:15
*/
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 服务器连接后被调用
* @author LYcreate
* @date 2019/10/14 22:08
* @param ctx
* @return void
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("true",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
/**
* 从服务器收到消息后调用
* @author LYcreate
* @date 2019/10/14 22:09
* @param ctx
* @param byteBuf
* @return void
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
System.out.println("Client received:" + byteBuf.toString(CharsetUtil.UTF_8));
}
}客户端引导类
package client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* @ClassName EchoClient
* @Description TODO
* @Author LYcreate
* @Date 2019/2/22 22:21
*/
public class EchoClient {
private final String host="http://localhost";
private final int port=8080;
public EchoClient(){};
public static void main(String[] args) {
new EchoClient().start();
}
public void start(){
EventLoopGroup group=new NioEventLoopGroup();
try{
Bootstrap bootstrap=new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(host,port)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture future=bootstrap.connect().sync();
future.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
}