第一个Netty程序
一、环境准备
Netty需要的运行环境很简单,只有2个。
- JDK 1.8+
- Apache Maven 3.3.9+
二、Netty 客户端/服务器概览
如图,展示了一个我们将要编写的 Echo 客户端和服务器应用程序。该图展示是多个客户端同时连接到一台服务器。所能够支持的客户端数量,在理论上,仅受限于系统的可用资源(以及所使用的 JDK 版本可能会施加的限制)。
Echo 客户端和服务器之间的交互是非常简单的;在客户端建立一个连接之后,它会向服务器发送一个或多个消息,反过来服务器又会将每个消息回送给客户端。虽然它本身看起来好像用处不大,但它充分地体现了客户端/服务器系统中典型的请求-响应交互模式。
三、编写 Echo 服务器
所有的 Netty 服务器都需要以下两部分。
- 至少一个 ChannelHandler—该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑。
- 引导—这是配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的端口上。
3.1 ChannelHandler 和业务逻辑
上一篇博文我们介绍了 Future 和回调,并且阐述了它们在事件驱动设计中的应用。我们还讨论了 ChannelHandler,它是一个接口族的父接口,它的实现负责接收并响应事件通知。
在 Netty 应用程序中,所有的数据处理逻辑都包含在这些核心抽象的实现中。因为你的 Echo 服务器会响应传入的消息,所以它需要实现ChannelInboundHandler 接口,用来定义响应入站事件的方法。简单的应用程序只需要用到少量的这些方法,所以继承 ChannelInboundHandlerAdapter 类也就足够了,它提供了ChannelInboundHandler 的默认实现。
我们将要用到的方法是:
- channelRead() :对于每个传入的消息都要调用;
- channelReadComplete() : 通知ChannelInboundHandler最后一次对channelRead()的调用是当前批量读取中的最后一条消息;
- exceptionCaught() :在读取操作期间,有异常抛出时会调用。
该 Echo 服务器的 ChannelHandler 实现是 EchoServerHandler,如代码:
package com.example.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* @author lhd
* @date 2023/05/16 15:05
* @notes Netty Echo服务端简单逻辑
*/
//表示channel可以并多个实例共享,它是线程安全的
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
//将消息打印到控制台
System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
//将收到的消息写给发送者,而不冲刷出站消息
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
//将未决消息冲刷到远程节点,并且关闭该 Channe
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
//打印异常堆栈跟踪
cause.printStackTrace();
//关闭该channel
ctx.