文章目录
Netty - 基本使用介绍
1.什么是Netty?
Netty是JBoss提供的高效的Java NIO开发框架。基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。
Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,采用了多路复用处理方式。
2.使用Netty的好处
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。
3.Netty基本使用
之前博客在介绍NIO以及Netty简述的时候也做了简单使用介绍,这里我们在真正使用Netty前,我们先来理解几个相关概念。
- Buffer
- org.jboss.netty.buffer包的接口及类,该包核心的接口是ChannelBuffer和ChannelBufferFactory,Netty使用ChannelBuffer来存储并操作读写的网络数据。ChannelBuffer除了提供和ByteBuffer类似的方法,还提供了 一些实用方法,具体可参考其API文档。
- Channel
- Channel主要提供的功能如下
- 1)当前Channel的状态信息,比如是打开还是关闭等。
- 2)通过ChannelConfig可以得到的Channel配置信息。
- 3)Channel所支持的如read、write、bind、connect等IO操作。
- 4)得到处理该Channel的ChannelPipeline,既而可以调用其做和请求相关的IO操作。
- 在Channel实现方面,以通常使用的nio socket来说,Netty中的NioServerSocketChannel和NioSocketChannel分别封装了java.nio中包含的 ServerSocketChannel和SocketChannel的功能。
- Channel主要提供的功能如下
- ChannelEvent
- Netty是事件驱动的,其通过ChannelEvent来确定事件流的方向。一个ChannelEvent是依附于Channel的 ChannelPipeline来处理,并由ChannelPipeline调用ChannelHandler来做具体的处理。
- ChannelPipeline
- Netty 在事件处理上,是通过ChannelPipeline来控制事件流,通过调用注册其上的一系列ChannelHandler来处理事件,这也是典型的***模式。
- NioEventLoopGroup
- 是用来处理I/O操作的多线程事件循环器,Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。在之前我们实现了一个服务端的应用,因此会有2个NioEventLoopGroup会被使用。第一个通常成为boss线程,负责监听端口建立连接。第二个成为worker线程,负责处理已建立的连接数据读写,当boss线程接收到连接,就会把连接信息注册到worker上。如何确定多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,并且可以通过构造函数来配置他们的关系。
pom.xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.10.5.Final</version>
</dependency>
ServerHandlerOne.java
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);
}
}
NettyServer.java
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...");
}
}
ClientHandlerOne.java
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);
}
}
NettyClient.java
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());
}
}
}
NettyClient运行结果:
Client start...
channelConnected
Please input:
send hello
ok!
NettyServer运行结果:
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实现原理浅析