Netty是JBoss提供的高效的Java NIO开发框架。基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。
Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,采用了多路复用处理方式。
Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。
Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发。“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如FTP、SMTP、HTTP、许多二进制和基于文本的传统协议,Netty在不降低开发效率、性能、稳定性、灵活性情况下,成功地找到了解决方案。
Netty目前使用的版本大致分为netty3.x 和 netty4.x、netty5.x。Hadoop、Dubbo、Akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x。Netty在游戏开发公司也应用广泛,最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x。
之前博客在介绍NIO以及Netty简述的时候也做了简单使用介绍,这里我们在真正使用Netty前,我们先来理解几个相关概念。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.10.5.Final</version>
</dependency>
package com.proto.server;
import org.jboss.netty.channel.*;
/**
* 消息接受处理类
* @author hzk
* @date 2018/8/16
*/
public class ServerHandlerOne extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
//接收数据
//ChannelBuffer channelBuffer = (ChannelBuffer)e.getMessage();
//System.out.println("Receive:"+new String(channelBuffer.array()));
//decoder
System.out.println((String)e.getMessage());
//回写数据
//encoder
ctx.getChannel().write("ok!");
super.messageReceived(ctx, e);
}
/**
* 捕获异常
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("exceptionCaught");
super.exceptionCaught(ctx, e);
}
/**
* 新连接 通常用来检测IP是否是黑名单
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelConnected");
super.channelConnected(ctx, e);
}
/**
* 必须是连接已经建立,关闭通道的时候才会触发,可以在用户断线的时候清除用户的缓存数据等(只有在连接建立后断开才会调用)
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelDisconnected");
super.channelDisconnected(ctx, e);
}
/**
* channel关闭的时候触发(无论连接是否成功都会调用关闭资源)
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelClosed");
super.channelClosed(ctx, e);
}
}
package com.proto.server;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Netty服务端
* @author hzk
* @date 2018/8/16
*/
public class NettyServer {
public static void main(String[] args){
//服务类引导
ServerBootstrap serverBootstrap = new ServerBootstrap();
//boss线程监听端口,worker线程负责数据读写
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//设置nio socket工厂
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boss,worker));
//设置管道的工厂
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//接收
pipeline.addLast("decoder",new StringDecoder());
//回写
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("serverHandlerOne",new ServerHandlerOne());
return pipeline;
}
});
serverBootstrap.bind(new InetSocketAddress(8888));
System.out.println("Netty server start...");
}
}
package com.proto.client;
import org.jboss.netty.channel.*;
/**
* 消息接受处理类
* @author hzk
* @date 2018/8/16
*/
public class ClientHandlerOne extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
//接收数据
//ChannelBuffer channelBuffer = (ChannelBuffer)e.getMessage();
//System.out.println("Receive:"+new String(channelBuffer.array()));
//decoder
System.out.println((String)e.getMessage());
super.messageReceived(ctx, e);
}
/**
* 捕获异常
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("exceptionCaught");
super.exceptionCaught(ctx, e);
}
/**
* 新连接 通常用来检测IP是否是黑名单
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelConnected");
super.channelConnected(ctx, e);
}
/**
* 必须是连接已经建立,关闭通道的时候才会触发,可以在用户断线的时候清除用户的缓存数据等(只有在连接建立后断开才会调用)
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelDisconnected");
super.channelDisconnected(ctx, e);
}
/**
* channel关闭的时候触发(无论连接是否成功都会调用关闭资源)
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelClosed");
super.channelClosed(ctx, e);
}
}
package com.proto.client;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Netty客户端
* @author hzk
* @date 2018/8/17
*/
public class NettyClient {
public static void main(String[] args){
//客户端引导
ClientBootstrap clientBootstrap = new ClientBootstrap();
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//设置nio socket工厂
clientBootstrap.setFactory(new NioClientSocketChannelFactory(boss,worker));
//设置管道的工厂
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("clientHandlerOne", new ClientHandlerOne());
return pipeline;
}
});
//连接服务端
ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress(8888));
Channel channel = connect.getChannel();
System.out.println("Client start...");
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("Please input:");
channel.write(scanner.next());
}
}
}
Client start...
channelConnected
Please input:
send hello
ok!
Netty server start...
channelConnected
sendhello
senhi
exceptionCaught
十月 30, 2018 6:49:02 下午 org.jboss.netty.channel.SimpleChannelHandler
警告: EXCEPTION, please implement com.proto.server.ServerHandlerOne.exceptionCaught() for proper handling.
java.io.IOException: 远程主机强迫关闭了一个现有的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:64)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108)
at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:337)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
channelDisconnected
channelClosed
Q:channelDisconnected与channelClosed的区别:
channelDisconnected:只有在连接建立后断开才会调用
channelClosed:无论连接是否成功都会调用关闭资源
这里推荐几篇博客给大家一起学习理解
Netty——基本使用介绍
Netty 5用户指南
Netty实现原理浅析