一、什么是Netty
Netty是一个NIO服务框架,简化了网咯应用框架的开发难度。Netty是一个事件驱动模型,(将很多阶段抽象成一个个的事件,让后将事件映射到多个回调方法上)。主要的特点有(https://netty.io/index.html):
二、Netty 的初级使用
public class TestServer{
public static void main(String[] args){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, wokerGroup)
.channel(NioServerSockerChannel.class)
.childHandler(new TestServerInitializer());
ChannelFuture future = bootstrap.bind(8899).sync();
future.channel().closeFuture().sync();
}
}
// 自定义启动初始化器
public class TestServerInitializer extens ChannelInitializer{
@Override
protected void initChannel(SocketChannel ch) throws Exception{
ChannelPipeline pipeline = ch.pipiline();
// HttpServerCodec是HttpRequestDecoder和HttpResponseDecoder的组合
pipeline.addLast("httpServerCodec", new HttpServerCodec());
pipeline.addLast("serverHandler", new TestServerHandler());
}
}
// 实现自己的回调方法
public class TestServerHandler extends SimpleChannelInboundHandler{
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg){
if(msg instanceof HttpObject){
ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
// response.headers().set()
ctx.writeAndFlush(response);
}
}
}
public class MyClient{
public static void main(String[] args){
EventLoopGroup loop = new NioEventLoopGroup();
try{
Bootstrap strap = new Bootstrap();
strap.group(loop).channel(NioSocketChannel.class)
.handler(new MyClientHandler());
ChannelFuture fut = strap.connect("localhost", 8899).sync();
fut.channel.closeFuture().sync();
}finally{
loop.shutdownGracefull();
}
}
}
public class MyClientInitializer extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel ch) throws Exception{
ChannelPipeline pipeline = ch.pipiline();
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast("my client handler", new MyClientHandler());
}
}
public class MyClientHandler extends SimpleChannelInboundHandler{
@Overrie
protected void channelRead0(ChannelHandlerContext ctx, String msg){
// 服务端向客户端传递数据时,客户端的处理逻辑
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
@Overrie
protected void channelActive(ChannelHandlerContext ctx){
// 当与服务端建立好连接后,操作
ctx.writeAndFlush("test");
}
}
2.1 SimpleChannelInboundHandler
处理流入channel的事件处理,常见的触发有
执行顺序是:channelAdded() ->registered()->active()->inactive()->unregistered
2.2 ChannelInitializer
// 自定义启动初始化器
public class TestServerInitializer extens ChannelInitializer{
@Override
protected void initChannel(SocketChannel ch) throws Exception{
ChannelPipeline pipeline = ch.pipiline();
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast("my handler", new MyHandler());
}
}
// 这里的泛型主要依据是请求的是什么样的格式数据
public class MyHandler extends SimpleChannelInboundHandler{
// 收到消息后的处理逻辑
@Overrie
protected void channelRead0(ChannelHandlerContext ctx, String msg){
}
}
三、聊天室实例
需求:
第一个客户端A与服务端建立连接后,服务端打印A上线;
后续的客户端与服务端建立好连接后,服务端打印X上线,并且广播X上线给已经建立好的连接。
客户端X发送一条消息后,广播到其余客户端
如果客户端下线后,服务端向所有的客户端广播X下线。
// 核心思想,利用channel的事件机制,将对应建立好的channel加入到一个组里维护
public class MyChatServerHandler extends SimpleChannelInboundHandler{
private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelRead0(ChannelHandlerContext ctx, String msg){
Channel channel = ctx.channel();
group.forEach(t -> {
if(channel != t){
t.writeAndFlush("非自己发出的消息");
}else{
t.writeAndFlush("自己发出的消息");
}
});
}
@Override
public void handlerAdded(ChannelHandlerContext ctx){
Channel ch = ctx.channel();
group.writeAndFlush(ch.remoteAddress() +"online");
group.add(ch);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx){
Chanel channel = ctx.channel();
group.writeAndFlush(channel.remoteAddress() + "offline");
// group.remove(channel); 这个netty会自动调用,所以可以省略
}
@Override
public void channelActive(ChannelHandlerContext ctx){
Channel channel = ctx.channel();
System.out.println(channel.remoteAddress() + "上线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx){
Channel channel = ctx.channel();
System.out.println(channel.remoteAddress() + "下线");
}
}