netty 4.x用户使用指南
引言
换句话说,Netty是一个NIO客户端服务器框架,它支持协议服务器和客户端等网络应用程序的快速轻松开发。它极大地简化和简化了TCP和UDP套接字服务器开发等网络编程。
“快速和简单”并不意味着最终的应用程序将遭受可维护性或性能问题的影响。Netty是根据从许多协议(如FTP、SMTP、HTTP以及各种基于二进制和文本的遗留协议)的实现中学到的经验精心设计的。因此,Netty成功地找到了一种无需妥协就可以轻松实现开发、性能、稳定性和灵活性的方法。
如果您更喜欢学习自顶向下的方法,那么您可能希望从第2章——体系结构概述开始,然后回到这里。
DISCARD。它是一种协议,在没有任何响应的情况下丢弃任何接收到的数据。要实现抛弃协议,惟一需要做的就是忽略所有接收到的数据。让我们直接从处理程序实现开始,它处理Netty生成的I/O事件。
package io.netty.example.discard; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; /**
* Handles a server-side channel.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1) @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.丢弃接收到的数据
((ByteBuf) msg).release(); // (3)
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
}
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; /**
* Discards any incoming data.丢掉所有进来的消息
*/
public class DiscardServer { private int port; public DiscardServer(int port) {
this.port = port;
} public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) 该对象相当于Socket中使用一个线程专门用户监听一个socket端口,然后将监听到的socket对象传入另一对象
EventLoopGroup workerGroup = new NioEventLoopGroup();// 该对象相当于Socket中对于每个socket连接都都单独开辟了一个线程进行数据解析出处理
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6) // Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7) // Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
注:1、NioEventLoopGroup是一个处理I/O操作的多线程事件循环。Netty为不同类型的传输提供了各种EventLoopGroup实现。在本例中,我们正在实现一个服务器端应用程序,因此将使用两个NioEventLoopGroup。第一个,通常被称为“老板”,接受进入的连接。第二个通常称为“worker”,在boss接受连接并将接受的连接注册给worker时,它将处理已接受连接的流量。使用多少线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
Channel设置服务器。但是,请注意,这是一个冗长的过程,在大多数情况下不需要这样做。恭喜你!你刚刚在Netty上完成了你的第一个服务器。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) { // (1)
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg); // (2)
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg); // (1)
ctx.flush(); // (2)
}
如果您再次运行telnet命令,您将看到服务器返回您发送给它的任何内容。
echo服务器的完整源代码位于发行版的io.net .example.echo包中。
package io.netty.example.time;
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(final ChannelHandlerContext ctx) { // (1)
final ByteBuf time = ctx.alloc().buffer(4); // (2)
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time); // (3)
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
assert f == future;
ctx.close();
}
}); // (4)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
3、像往常一样,我们编写构造好的消息。
但是等等,抛硬币在哪里?在使用NIO发送消息之前,我们不是曾经调用java.nio.ByteBuffer.flip()吗?ByteBuf没有这样的方法,因为它有两个指针;一个用于读操作,另一个用于写操作。当您向ByteBuf写入内容时,写入器索引会增加,而读取器索引不会改变。阅读器索引和写入器索引分别表示消息开始和结束的位置。
相反,NIO缓冲区没有提供一种干净的方法来确定消息内容在哪里开始和结束,而不调用flip方法。当您忘记翻转缓冲区时,您将遇到麻烦,因为不会发送任何或不正确的数据。在Netty中不会发生这样的错误,因为对于不同的操作类型,我们有不同的指针。当你习惯了它,你会发现它让你的生活变得更容易——一个没有翻转的生活!
要注意的另一点是ChannelHandlerContext.write()(和writeAndFlush())方法返回ChannelFuture。ChannelFuture表示尚未发生的I/O操作。这意味着,由于Netty中的所有操作都是异步的,因此可能还没有执行任何请求的操作。例如,以下代码可能会在发送消息之前关闭连接:
Channel ch = ...;
ch.writeAndFlush(message);
ch.close();
f.addListener(ChannelFutureListener.CLOSE);
要测试我们的时间服务器是否按预期工作,您可以使用UNIX rdate命令:
$ rdate -o <port> -p <host>
在Netty中,服务器和客户机之间最大也是唯一的区别是使用了不同的引导和通道实现。请查看以下代码:
package io.netty.example.time;
public class TimeClient {
public static void main(String[] args) throws Exception {
String host = args[0];
int port = Integer.parseInt(args[1]);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync(); // (5)
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
2、如果只指定一个EventLoopGroup,它将作为boss组和工作者组使用。但是,老板员工并不用于客户端
package io.netty.example.time;
import java.util.Date;
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg; // (1)
try {
long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
} finally {
m.release();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}



TIME客户端示例。我们在这里遇到同样的问题。32位整数是非常少量的数据,并且不太可能经常被分段。然而,问题在于它可能是碎片化的,并且随着流量的增加,碎片化的可能性将增加。TimeClientHandler修复此问题的修改实现:
package io.netty.example.time;
import java.util.Date;
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private ByteBuf buf;
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
buf = ctx.alloc().buffer(4); // (1)
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
buf.release(); // (1)
buf = null;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg;
buf.writeBytes(m); // (2)
m.release();
if (buf.readableBytes() >= 4) { // (3)
long currentTimeMillis = (buf.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
TIME客户端的问题,但修改后的处理程序看起来并不干净。想象一个更复杂的协议,它由多个字段组成,例如可变长度字段。您的ChannelInboundHandler实施将很快变得无法维护。ChannelHandler为a 添加多个ChannelPipeline,因此,您可以将一个单片拆分ChannelHandler为多个模块化,以降低应用程序的复杂性。例如,您可以拆分TimeClientHandler为两个处理程序:TimeDecoder它涉及碎片问题,以及- 最初的简单版本
TimeClientHandler
幸运的是,Netty提供了一个可扩展的类,可以帮助您编写第一个开箱即用的类:
package io.netty.example.time;
public class TimeDecoder extends ByteToMessageDecoder { // (1)
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { // (2)
if (in.readableBytes() < 4) {
return; // (3)
}
out.add(in.readBytes(4)); // (4)
}
}
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeDecoder(), new TimeClientHandler());
}
});
如果你是一个喜欢冒险的人,你可能想试试ReplayingDecoder,这将解码器变得更加简单。不过,您需要参考API参考以获得更多信息。
public class TimeDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(
ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
out.add(in.readBytes(4));
}
}
io.netty.example.factorial基于二进制协议io.netty.example.telnet基于文本行的协议.
ChannelHandler中使用POJO的优势是显而易见的; 通过分离ByteBuf从处理程序中提取信息的代码,您的处理程序变得更易于维护和重用。在TIME客户端和服务器示例中,我们只读取一个32位整数,这不是ByteBuf直接使用的主要问题。但是,您会发现在实现真实世界协议时必须进行分离。UnixTime。
package io.netty.example.time;
import java.util.Date;
public class UnixTime {
private final long value;
public UnixTime() {
this(System.currentTimeMillis() / 1000L + 2208988800L);
}
public UnixTime(long value) {
this.value = value;
}
public long value() {
return value;
}
@Override
public String toString() {
return new Date((value() - 2208988800L) * 1000L).toString();
}
}
我们现在可以修改它TimeDecoder来产生一个UnixTime而不是一个ByteBuf。
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 4) {
return;
}
out.add(new UnixTime(in.readUnsignedInt()));
}
使用更新的解码器,TimeClientHandler不再使用ByteBuf:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
UnixTime m = (UnixTime) msg;
System.out.println(m);
ctx.close();
}
更简单,更优雅,对吧?可以在服务器端应用相同的技术。我们TimeServerHandler这次更新第一次:
@Override
public void channelActive(ChannelHandlerContext ctx) {
ChannelFuture f = ctx.writeAndFlush(new UnixTime());
f.addListener(ChannelFutureListener.CLOSE);
}
现在,唯一缺少的部分是一个编码器,它的实现ChannelOutboundHandler将一个UnixTime转换为一个ByteBuf。它比编写解码器简单得多,因为编码消息时无需处理数据包碎片和汇编。
package io.netty.example.time;
public class TimeEncoder extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
UnixTime m = (UnixTime) msg;
ByteBuf encoded = ctx.alloc().buffer(4);
encoded.writeInt((int)m.value());
ctx.write(encoded, promise); // (1)
}
}
MessageToByteEncoder:
public class TimeEncoder extends MessageToByteEncoder<UnixTime> {
@Override
protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) {
out.writeInt((int)msg.value());
}
}
Future。io.netty.example包中的Netty示例。netty 4.x用户使用指南的更多相关文章
- "WannaCry"勒索病毒用户处置指南
"WannaCry"勒索病毒用户处置指南 原文: http://mp.weixin.qq.com/s/ExsribKum9-AN1ToT10Zog 卡巴斯基,下载官网:h ...
- 云资源中的低成本战斗机——竞价实例,AWS、阿里云等六家云厂商完全用户使用指南
云端资源价格 预留实例:长期持有,批发路线,价格最便宜. 按需实例:即买即用,零售路线,价格最贵. 这两种资源,基于不同区域/价格的六家云厂商价格对比,连同原始数据文档我们已经打包成了一份电子文档,有 ...
- Java Netty 4.x 用户指南
问题 今天,我们使用通用的应用程序或者类库来实现互相通讯,比如,我们经常使用一个 HTTP 客户端库来从 web 服务器上获取信息,或者通过 web 服务来执行一个远程的调用. 然而,有时候一个通用的 ...
- tween用户使用指南
tween.js user guide tween.js用户指南 1.What is a tween? How do they work? Why do you want to use them? 一 ...
- netty集成ssl完整参考指南(含完整源码)
虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于open的考虑.中午特地测了下netty下集成ss ...
- Gradle用户使用指南
转载请事先沟通,未经允许,谢绝转载. 1. 新工具介绍(Introduction) 能够复用代码和资源能够构建几种不同版本参数的应用能够配置.扩展.自定义构建过程1.1 为什么选择Gradle(Why ...
- 阿里云 EDAS-HSF 用户指南
阿里云 EDAS-HSF 用户指南 针对 EDAS v2.3.0©Alibaba EDAS 项目组2015/8/19 1 前言本文档旨在描述阿里云 EDAS 产品中应用服务化模块的基本概念,以及如何使 ...
- 《Netty权威指南》目录
一.基础篇 走进Java NIO 1. Java 的 I/O 演进之路:https://www.cnblogs.com/zengzhihua/p/9930652.html 2. NIO 入门:http ...
- 《Apache Velocity用户指南》官方文档
http://ifeve.com/apache-velocity-dev/ <Apache Velocity用户指南>官方文档 原文链接 译文连接 译者:小村长 校对:方腾飞 Qui ...
随机推荐
- Vue使用better-scroll左右菜单联动
说明 最近想做一个vue商城小项目,练习一下vue的语法,刚刚好碰到了需要左右菜单实现联动,因此就接触了 better-scroll. github地址 中文文档. 代码 页面结构以及数据 //页面结 ...
- Docker(三):利用Kubernetes实现容器的弹性伸缩
一.前言 前两章有的介绍docker与Kubernetes.docker是项目运行的容器,Kubernetes则是随着微服务架构的演变docker容器增多而进行其编排的重要工具.Kubernetes不 ...
- 在Mysql中,事务是如何实现的呢?
hello大家好,我是一个爱看底层的小码,对于每一个学习mysql数据库的同学来说,事务都是一个绕不开的话题,简单的说来事务是指访问并可能更新数据库中各项数据项的一个程序执行单元.事务的四个特征无非就 ...
- ubuntu以root身份登录
- 遇到的一些在ie下的兼容问题和解决方案(ie10+)
1,ie 10下实现水平垂直居中,不固定高度的话,正常的top:50%,left:50%,transform(translate(-50%,-50%)) 是不能实现的,ie下top:50%会失去效果. ...
- JAVA byte数组转化为16进制字符串输出
最简单的方法: 利用javax.xml.bind包下的DatatypeConverter printHexBinary public static java.lang.String printHexB ...
- 了解EBP指针
在寄存器里面有很多寄存器虽然他们的功能和使用没有任何的区别,但是在长期的编程和使用中,在程序员习惯中已经默认的给每个寄存器赋上了特殊的含义,比如:EAX一般用来做返回值,ECX用于记数等等.在win3 ...
- java4选择结构 二
public class jh_01_为什么使用switch选择结构 { /* * 韩嫣参加计算机编程大赛 * 如果获得第一名,将参加麻省理工大学组织的1个月夏令营 * 如果获得第二名,将奖励惠普笔记 ...
- tomcat-windows10环境搭建
1.进入Tomcat官网Apache Tomcat® - Welcome! 2.根据操作系统选择合适的版本下载 zip用于windows操作系统, tar.gz用于unix和linux操作系统 Bin ...
- JFrame的getContentPane
我们可以在 JFrame 对象中添加 AWT 或者 Swing 组件.但是,虽然它有 add 方法,却不能直接用于添加组件,否则会抛出异常.造成这个现象的原因只有一个解释:JFrame 不是一个容器, ...