netty Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
更确切的讲是一个组件,没有那么复杂。

例子 一  Discard服务器端

我们先写一个简单的服务端和客户端作为入门,接下来我们在深入介绍里面的内容 :(基于netty4 )

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();
}
}

这是服务端的处理引擎,继承于ChannelInboundHandlerAdapter,但也可以实现接口ChannelInboundHandler,不过需要实现很多方法。channelRead() 是服务器接收到客户端的数据并处理的,读取的方式可以是bytebuf二进制缓存,也可以是pojo实体,这部分在下一章介绍。

ByteBuf是一个引用计数对象,必须通过release()方法显式释放。 请记住,处理程序有责任释放传递给处理程序的引用计数对象。 通常,channelRead()处理方法的实现方式 如下:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
}

exceptionCaught() 是异常处理方法,通常是日志记录,异常具体处理,如发送错误信息并关闭连接等操作。

接下来,我们编写服务端的主程序:

package io.netty.example.discard;

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)
EventLoopGroup workerGroup = new NioEventLoopGroup();
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。 第一个,通常称为“老板”,接受传入的连接。 第二个,通常称为“工人”,一旦老板接受连接并将接受的连接注册到工作人员,就处理接受的连接的流量。 使用多少个线程以及它们如何映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。

2,ServerBootstrap是一个帮助类,用于设置服务器。 您可以直接使用Channel设置服务器。 但是,请注意,这是一个繁琐的过程,在大多数情况下您不需要这样做。

3,在这里,我们指定使用NioServerSocketChannel类来实例化一个新的通道来接受传入的连接。

4,这里指定的处理程序将始终由新接受的Channel进行评估。 ChannelInitializer是一个特殊的处理程序,旨在帮助用户配置新的Channel。 很可能您想通过添加一些处理程序(如DiscardServerHandler)来配置新通道的ChannelPipeline来实现您的网络应用程序。 随着应用程序变得复杂,您可能会在ChannelPipeline中添加更多的处理程序,并将这个匿名类最终提取到顶级类中。

5,您还可以设置特定于Channel实现的参数。 我们正在编写一个TCP / IP服务器,因此我们可以设置套接字选项,如tcpNoDelay和keepAlive。 请参阅ChannelOption的apidocs和特定的ChannelConfig实现,以获得有关支持的ChannelOptions的概述。

6,你有没有注意到option()和childOption()? option()用于接受传入连接的NioServerSocketChannel。 childOption()用于在这种情况下由主服务器通道(即NioServerSocketChannel)接受的通道。

7,我们现在准备好了。 剩下的是绑定到端口并启动服务器。 这里,我们绑定到机器中所有NIC(网络接口卡)的端口8080。 您现在可以根据需要调用bind()方法多次(使用不同的绑定地址)。

恭喜! 你刚刚完成了Netty上的第一台服务器。

========================================================

如何查看接收到的数据

现在我们已经写了我们的第一个服务器,我们需要测试它是否真的有效。 测试它的最简单的方法是使用telnet命令。 例如,您可以在命令行中输入telnet localhost 8080,然后输入发送内容。

但是如何证明服务器收到了信息了呢?我们已经知道,在收到数据时,会调用channelRead()方法。 让我们把一些代码放到DiscardServerHandler的channelRead()方法中:

@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)
}
}

1,这个无效循环实际上可以简化为:System.out.println(in.toString(io.netty.util.CharsetUtil.US_ASCII))

2,可选的操作:in.release() 释放资源

接下来,你就可以看到打印的信息了。

========================================================

写一个回显的服务器

到目前为止,我们一直在消费数据,没有任何反应。 然而,服务器通常应该响应请求。 让我们学习如何通过实现ECHO协议向客户端写入响应消息,其中任何接收到的数据都将被发回。与前面部分实现的丢弃服务器的唯一区别在于它将接收到的数据发回,而不是将接收到的数据输出到控制台。 因此,再次修改channelRead()方法是足够的:

 @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg); // (1)
ctx.flush(); // (2)
}

1,ChannelHandlerContext对象提供了各种操作,可以触发各种I / O事件和操作。 在这里,我们调用write(Object)来逐字写入接收到的消息。 请注意,我们没有像DISCARD示例中那样发布接收的消息。 这是因为,当Netty发布给电子邮件时,Netty会为您发布。

2,ctx.write(Object)不会将消息写入管道。 它在内部缓冲,然后通过ctx.flush()刷出数据。 或者,为简洁起见,您可以调用ctx.writeAndFlush(msg)。

===========================================================

 写一个时间服务器

本节中实现的协议是TIME协议。 与前面的示例不同的是,它发送一个包含32位整数的消息,而不接收任何请求,并在发送消息后关闭连接。 在此示例中,您将学习如何构建和发送消息,并在完成时关闭连接。

因为我们将忽略任何接收到的数据,而是在建立连接后立即发送消息,这次我们不能使用channelRead()方法。 相反,我们应该覆盖channelActive()方法。 以下是实现:

服务引擎:

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();
}
}

1,如上所述,当建立连接并准备好生成连接时,将调用channelActive()方法。 我们来写一个32位整数,表示这个方法当前的时间。

2,要发送新消息,我们需要分配一个包含消息的新缓冲区。 我们要写一个32位整数,因此我们需要一个容量至少为4个字节的ByteBuf。 通过ChannelHandlerContext.alloc()获取当前的ByteBufAllocator,并分配一个新的缓冲区。

3,接下来,我构造消息:

但等等,消息切割在哪里?在NIO发送消息之前,我们是否曾经调用过java.nio.ByteBuffer.flip()? ByteBuf没有这样的方法,因为它有两个指针;一个用于读操作,另一个用于写操作。当读写器索引没有改变时,写入索引会增加。读者索引和写索引分别表示消息的开始和结束位置。

相比之下,NIO缓冲区不能提供一个干净的方式来确定消息内容的起始和结束位置,而不需要调用flip方法。当您忘记翻转缓冲区时,您将会遇到麻烦,因为没有发送任何数据或不正确的数据。在Netty中不会发生这样的错误,因为我们针对不同的操作类型有不同的指针。你会发现它使你的操作更简单。

另外要注意的是,ChannelHandlerContext.write()(和writeAndFlush())方法返回一个ChannelFuture。 ChannelFuture表示尚未发生的I / O操作。这意味着任何请求的操作可能尚未执行,因为所有操作在Netty中都是异步的。例如,即使在发送消息之前,以下代码也可能会关闭连接:

Channel ch = ...;
ch.writeAndFlush(message);
ch.close();

因此,您需要在ChannelFuture完成之后调用close()方法,该方法由write()方法返回,并且在写入操作完成后通知其监听器。 请注意,close()也可能不会立即关闭连接,并返回ChannelFuture。

4,当写请求完成后,我们如何得到通知? 这就像将ChannelFutureListener添加到返回的ChannelFuture一样简单。 在这里,我们创建了一个新的匿名ChannelFutureListener,当操作完成时关闭通道。

f.addListener(ChannelFutureListener.CLOSE);

要测试我们的时间服务器是否按预期工作,可以使用UNIX rdate命令:

$ rdate -o <port> -p <host>

其中<port>是在main()方法中指定的端口号,<host>通常是localhost。

=======================================================================

编写一个时间的客户端

与DISCARD服务器和回显服务器不同,我们需要TIME协议的客户端,因为人类无法将32位二进制数据转换为日历上的日期。 在本节中,我们将讨论如何确保服务器正常工作,并学习如何使用Netty编写客户端。

Netty中服务器和客户端之间最大和唯一的区别是使用不同的Bootstrap和Channel实现。 请看下面的代码:

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();
}
}
}

1,Bootstrap类似于ServerBootstrap,除了它用于非服务器通道,如客户端或无连接通道。

2,如果只指定一个EventLoopGroup,它将同时用作一个老板组和一个工作组。 虽然老板的工作人员不用于客户端。

3,NioSocketChannel代替NioServerSocketChannel,用于创建客户端channel。

4,请注意,我们不使用childOption(),这与ServerBootstrap不同,因为客户端SocketChannel没有父级。

5,我们应该调用connect()方法而不是bind()方法。

可以看到,它与服务器端代码没有什么不同。 ChannelHandler的实现如何? 它应该从服务器收到一个32位整数,将其翻译成可读的格式,打印翻译时间,并关闭连接:

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();
}
}

1,在TCP / IP中,Netty将从对等体发送到ByteBuf的数据。

它看起来很简单,与服务器端的示例看起来没有什么不同。 但是,这个处理程序有时会拒绝提高IndexOutOfBoundsException。 我们讨论为什么会在下一节中发生这种情况。

===================================================

总结:这主要翻译官方文献,内容还是很清晰的。

地址:http://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-11

也可以官方下载相应的例子进行研究

netty 入门(一)的更多相关文章

  1. Netty入门之客户端与服务端通信(二)

    Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...

  2. Netty入门之HelloWorld

    Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ...

  3. Netty入门

    一.NIO Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,在jdk1.4 里提供的新api,他的他的特性 ...

  4. netty入门(一)

    1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ...

  5. Netty入门(三)之web服务器

    Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ...

  6. Netty入门(二)之PC聊天室

    参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...

  7. Netty入门(一)之webSocket聊天室

    一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...

  8. netty同时做http和websocket(netty入门)

    ---恢复内容开始--- http://www.jianshu.com/p/5c29c6c6d28c ---恢复内容结束--- http://www.jianshu.com/p/5c29c6c6d28 ...

  9. Netty入门教程——认识Netty

    什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...

  10. Netty 系列(三)Netty 入门

    Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...

随机推荐

  1. 使用Travis CI自动部署Hexo到GitHub

    原文链接(转载请注明出处):使用Travis CI自动部署Hexo到GitHub 前言 使用 hexo + gitPages 搭建个人博客的人都知道,每当要发表一篇博文,第一步得手动使用 hexo g ...

  2. c#枚举 获取枚举键值对、描述等

    using System; using System.Collections.Generic; using System.Collections.Specialized; using System.C ...

  3. 发布系统Git使用指南 - the Git Way to Use Git

    发布系统Git使用指南 --the Git Way to Use Git 背景 ​ 有文章曾归纳,Git是一套内容寻址文件系统,意思是,Git的核心是存储键值对^[1]^.显然,这样的形式不利于普通人 ...

  4. Linux基本符号

    Linux环境下一些常用的符号 ; 多个命令的分隔符 / 根目录或路径分隔符 > 重定向,数据沿箭头方向流动,原来文件内容会被丢弃 >> 追加重定向,在原来文件结尾追加内容 .. 上 ...

  5. EF框架搭建小总结--ModelFirst模型优先

    前言:去年刚工作的时候,也是刚刚正式接触.net,当时了解了EF以及三种开发模式,Database First.Model First .Code First.公司用的开发模式是Database Fi ...

  6. win10 uwp 反射

    本文在h神的指导下完成. 反射是强大的好用的,我们可以添加新功能不修改之前的代码,通过使用反射得到. 本文下面和大家说如何做一个和WPF一样的反射功能,如何才能获的 UWP 程序集所有类. 先来说下反 ...

  7. VIM文本替换命令

    在VIM中进行文本替换:       1.  替换当前行中的内容:    :s/from/to/    (s即substitude)         :s/from/to/     :  将当前行中的 ...

  8. Prometheus 到底 NB 在哪里?- 每天5分钟玩转 Docker 容器技术(84)

    本节讨论 Prometheus 的核心,多维数据模型.我们先来看一个例子. 比如要监控容器 webapp1 的内存使用情况,最传统和典型的方法是定义一个指标 container_memory_usag ...

  9. canvas绘制太阳系

    原文地址:http://jeffzhong.space/2017/10/26/solar/ 学习canvas有一段时间了,顺便写个小项目练手,该项目用到的知识点包括: ES6面向对象 基本的三角函数 ...

  10. 在Owin Self-Hosing下实现每个请求中共享上下文(数据)

    问题 这几天在做公司的外部WebApi网关,由于使用了OAuth2.0,所以不得不使用Owin来部署网关. 而涉及到请求上下文的问题,为了使业务层能获取到请求头的信息,又不与网关耦合,决定把请求信息写 ...