netty官方文档

netty 官网首页
netty 4.x 官方文档

官方首页

在这里插入图片描述

问题

如今,我们使用通用应用程序或库来相互通信。例如,我们经常使用 HTTP 客户端库从 Web 服务器检索信息并通过 Web 服务调用远程过程调用。但是,通用协议或其实现有时不能很好地扩展。这就像我们不使用通用 HTTP 服务器来交换大型文件、电子邮件消息和近实时消息(如财务信息和多人游戏数据)。需要的是专门用于特殊目的的高度优化的协议实现。例如,您可能希望实现一个针对基于 AJAX 的聊天应用程序、媒体流或大文件传输进行了优化的 HTTP 服务器。您甚至可能想要设计和实施一个完全适合您需要的全新协议。另一个不可避免的情况是当您必须处理遗留的专有协议以确保与旧系统的互操作性时。在这种情况下,重要的是我们在不牺牲结果应用程序的稳定性和性能的情况下能够以多快的速度实现该协议。

解决方案

Netty 项目致力于提供异步事件驱动的网络应用程序框架和工具,用于快速开发可维护的高性能和高可扩展性协议服务器和客户端。

换句话说,Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。它极大地简化了网络编程,例如 TCP 和 UDP 套接字服务器的开发

“快速和简单”并不意味着生成的应用程序会受到可维护性或性能问题的影响。Netty 是根据从许多协议(如 FTP、SMTP、HTTP 和各种二进制和基于文本的旧协议)的实施中获得的经验精心设计的。因此,Netty 成功地找到了一种方法,可以在不妥协的情况下实现易于开发、性能、稳定性和灵活性。

一些用户可能已经找到了声称具有相同优势的其他网络应用程序框架,您可能想问是什么让 Netty 与它们如此不同。答案是它所建立的哲学。Netty 旨在从一开始就在 API 和实施方面为您提供最舒适的体验。它不是有形的,但您会意识到,当您阅读本指南并使用 Netty 时,这种理念将使您的生活变得更加轻松。

入门

本章通过简单的示例介绍 Netty 的核心结构,让您快速入门。读完本章后,您将能够立即在 Netty 之上编写客户端和服务器。

如果您更喜欢自上而下的学习方法,您可能希望从第 2 章“架构概述”开始,然后回到这里。

编写第一个服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.taopanfeng.junit.netty.example.discard;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
* 处理服务器端通道。
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
try {
// Do something with msg
// 输出一下内容
ByteBuf in = (ByteBuf) msg;
System.err.println("===> 接收到数据:" + in.toString(Charset.forName("GBK")));
} finally {
// 静默丢弃接收到的数据。
// ((ByteBuf) msg).release(); // (3)
ReferenceCountUtil.release(msg);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// 引发异常时关闭连接。
cause.printStackTrace();
ctx.close();
}
}
  1. DiscardServerHandler extends ChannelInboundHandlerAdapter,它是ChannelInboundHandler. ChannelInboundHandler提供可以覆盖的各种事件处理程序方法。ChannelInboundHandlerAdapter目前,扩展而不是自己实现处理程序接口就足够了。
  2. 我们在这里覆盖channelRead()事件处理程序方法。每当从客户端接收到新数据时,都会使用接收到的消息调用此方法。在这个例子中,接收到的消息的类型是ByteBuf。
  3. 为了实现该DISCARD协议,处理程序必须忽略接收到的消息。ByteBuf是一个引用计数的对象,必须通过该release()方法显式释放。请记住,处理程序有责任释放传递给处理程序的任何引用计数对象。通常,channelRead()处理程序的方法是 try-finally。
  4. 当exceptionCaught()Netty 由于 I/O 错误或由于处理事件时抛出的异常而由处理程序实现引发异常时,使用 Throwable 调用事件处理程序方法。在大多数情况下,应记录捕获的异常并在此处关闭其关联的通道,尽管此方法的实现可能会有所不同,具体取决于您要如何处理异常情况。例如,您可能希望在关闭连接之前发送带有错误代码的响应消息。

到目前为止,一切都很好。我们已经实现了DISCARD服务器的前半部分。现在剩下的就是编写main()使用DiscardServerHandler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.taopanfeng.junit.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;

/**
* 丢弃任何传入数据。
*/
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)

// 绑定并开始接受传入的连接。
ChannelFuture f = b.bind(port).sync(); // (7)

// 等到服务器套接字关闭。在此示例中,这不会发生,但您可以这样做以正常关闭服务器。
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

public static void main(String[] args) throws Exception {
int port = 8080;

new DiscardServer(port).run();

// 0. 启动 main 方法
// 1. 连接 => telnet 127.0.0.1 8080
// 2. 进入发送 => Ctrl + ]
// 3. 发送 => send "Hello 陶攀峰!!!"
// 4. 退出 => q
}
}
  1. NioEventLoopGroup是一个处理 I/O 操作的多线程事件循环。NettyEventLoopGroup为不同类型的传输提供了各种实现。在此示例中,我们正在实现一个服务器端应用程序,因此NioEventLoopGroup将使用两个。第一个,通常称为“boss”,接受传入连接。第二个,通常称为“worker”,一旦老板接受连接并将接受的连接注册到工作人员,就会处理接受的连接的流量。使用了多少 Thread 以及它们如何映射到 createdChannel取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
  2. ServerBootstrap是一个设置服务器的助手类。您可以直接使用 a 设置服务器Channel。但是,请注意,这是一个乏味的过程,在大多数情况下您不需要这样做。
  3. 在这里,我们指定使用NioServerSocketChannel用于实例化一个新的类Channel来接受传入的连接。
  4. 此处指定的处理程序将始终由新接受的Channel. 这ChannelInitializer是一个特殊的处理程序,旨在帮助用户配置新的Channel. 您很可能希望通过添加一些处理程序来配置ChannelPipeline新Channel的,例如DiscardServerHandler实现您的网络应用程序。随着应用程序变得复杂,您可能会向管道添加更多处理程序,并最终将这个匿名类提取到顶级类中。
  5. 您还可以设置特定于Channel实现的参数。我们正在编写一个 TCP/IP 服务器,因此我们可以设置套接字选项,例如tcpNoDelay和keepAlive。请参阅 apidocsChannelOption和具体ChannelConfig实现,以了解有关支持ChannelOption的 s 的概述。
  6. 你注意到option()和了childOption()吗?option()用于NioServerSocketChannel接受传入连接的 。childOption()是为Channels 接受的 parent ServerChannel,NioSocketChannel在这种情况下。
  7. 我们现在准备出发。剩下的就是绑定到端口并启动服务器。在这里,我们绑定到8080机器中所有 NIC(网络接口卡)的端口。您现在可以bind()根据需要多次调用该方法(使用不同的绑定地址。)

恭喜!您刚刚在 Netty 上完成了您的第一台服务器。
在这里插入图片描述
【日期标记】2022-08-04 09:56:57 以上同步完成