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 ...
随机推荐
- 手把手写框架入门(一) | 核心拦截器DispatchFilter实现
前言 1Filter实现框架拦截 1配置自定义Filter 2创建一个Filter 3创建一个ActionMapping 4创建一个ActionMapper 5创建一个WebExecutor 6创建测 ...
- DirectX11 With Windows SDK--29 计算着色器:内存模型、线程同步;实现顺序无关透明度(OIT)
前言 由于透明混合在不同的绘制顺序下结果会不同,这就要求绘制前要对物体进行排序,然后再从后往前渲染.但即便是仅渲染一个物体(如上一章的水波),也会出现透明绘制顺序不对的情况,普通的绘制是无法避免的.如 ...
- js中函数this的指向
this 在面试中,js指向也常常被问到,在开发过程中也是一个需要注意的问题,严格模式下的this指向undefined,这里就不讨论. 普通函数 记住一句话哪个对象调用函数,该函数的this就指向该 ...
- supervisor守护filebeat服务进程
1.变更原因 部署安装supervisor进行filebeat守护及后面的各种服务进程守护可以用2.变更内容增加supervisor服务 3.变量时间:6月2号-6月3号4.变更风险评估:无风险4.1 ...
- pdo数据操作,3-4,0724
require 'connect.php'; $linshi = $dbh->prepare('UPDATE `category` SET `name` = :name, `alias`=:al ...
- Apache-Tomcat-Ajp漏洞(CVE-2020-1938)漏洞复现
前言 Apache Tomcat会开启AJP连接器,方便与其他Web服务器通过AJP协议进行交互.由于Tomcat本身也内含了HTTP服务器,因此也可以视作单独的Web服务器.此漏洞为文件包含漏洞,攻 ...
- Doug Hennig的自定义 DataEnvironment 和 CursorAdapter 类文件 -- SFDataClasses
Doug Hennig的自定义 DataEnvironment 和 CursorAdapter 类文件 -- SFDataClasses.vcx,其中包括:SFCursorAdapter 和 SFDa ...
- 使用docker搭建FastDFS
拉取镜像(使用docker-componse可以忽略) [root@localhost ~]# docker pull phinexdaz/fdfs_tracker [root@localhost ~ ...
- [Linux]curl 获取本服务器公网IP
curl ifconfig.me curl icanhazip.com curl curlmyip.com curl ip.appspot.com curl ipinfo.io/ip curl ipe ...
- WARNING: The host '$hostname' could not be looked up with resolveip. (转)
环境介绍:CentOS6.X MySQL版本:5.5.X以上 执行scripts/mysql_install_db脚本时,抛出一条Warning,主机名和IP地址无法解析: The host '$ho ...