小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
上一篇简单的介绍了Netty相关特性等,这篇文章来写个入门的Netty应用程序,并竟先不懂才会想着去学习不。
原本是打算先写理论知识的,但是想了想,还是觉得应该先写代码,然后再通过代码和流程图,才更好去分析Netty中的组件和机制。
所以就让我们一起来写出属于我们的第一款Netty应用程序吧。
一、编程步骤:
- 建立一个Maven项目。
- 导入依赖
- 编写Netty服务端
- 编写一个ChannelHandler(服务器用来对从客户端接收的数据的处理)和业务逻辑
- 编写一个Server启动类
- 编写Netty客户端
- 编写一个ChannelHadler处理客户端逻辑
- 引导客户端
- 最后启动测试(打卡下班啦啦啦😁)
二、导入依赖:
我在这里偷懒了,直接是导入了netty-all
,如果不想的话,大家可以用到什么导入什么,因为Netty支持的特别广,所以有不同的Jar包。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.65.Final</version>
</dependency>
三、编写服务器端:
先编写ServerHandler
,处理业务逻辑的。
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: crush
* @Date: 2021-08-22 10:19
* version 1.0
* @deprecated @Sharable 表示可以将带注释的ChannelHandler的同一实例多次添加到一个或多个ChannelPipeline ,而不会出现竞争条件。
*/
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 对于每个传入的消息都要调用
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
//将消息记录到控制台
System.out.println(
"Server received: " + in.toString(CharsetUtil.UTF_8));
// 将接收到的消息写给发送者,而不冲刷出站消息
ctx.write(in);
}
/**
* —通知ChannelInboundHandler 最后一次对channelRead()的调用是当前批量读取中的最后一条消息
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
// 将消息发送到远程节点,并且关闭该 Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
/**
* 在读取操作期间,有异常抛出时会调用
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
服务器端启动类:
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
int port=8081;
new EchoServer(port).start();
}
public void start() throws Exception {
final EchoServerHandler serverHandler = new EchoServerHandler();
//创建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//它允许轻松引导ServerChannel
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
/**用于从中创建Channel实例的Class */
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
//添加一个EchoServerHandler 到子Channel的 ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
//处理 I/O 事件或拦截 I/O 操作,并将其转发到其ChannelPipeline下一个处理程序
@Override
public void initChannel(SocketChannel ch)
throws Exception {
//EchoServerHandler 被标注为@Shareable,所以我们可以总是使用同样的实例
ch.pipeline().addLast(serverHandler);
}
});
/**
* 异步地绑定服务器;调用 sync()方法阻塞等待直到绑定完成
*/
ChannelFuture f = b.bind().sync();
/**
* 获取 Channel 的CloseFuture,并且阻塞当前线程直到它完成
*/
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully();
}
}
}
四、编写客户端:
EchoClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import java.util.Scanner;
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 在到服务器的连接已经建立之后将被调用;
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
//当被通知 Channel是活跃的时候,发送一条消息
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks! 你好丫!!" ,
CharsetUtil.UTF_8));
}
/**
* 当从服务器接收到一条消息时被调用;
*/
@Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
// 记录已接收消息的转储
System.out.println(
"Client received: " + in.toString(CharsetUtil.UTF_8));
}
/**
* 在处理过程中引发异常时被调用。
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
EchoClient
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
//指定 EventLoopGroup 以处理客户端事件;需要适用于 NIO 的实现
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
//在创建Channel时,向 ChannelPipeline中添加一个 EchoClientHandler 实例
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
String host="localhost";
int port=8081;
new EchoClient(host, port).start();
}
}
五、测试
先启动服务端
再启动客户端:
六、结语
看完这一章,如果是第一次接触,肯定还是会很懵的。
我的打算是想让大家先了解代码,再画出流程图,再进一步分析。
文章持续更新中,明天就更。
所以想了解 关于 Netty工作流程和核心组件 👈 (先占个坑👨💻)