在之前系列的介绍了NIO相关的文章之后,我们可以在此基础上进行Netty的学习了。建议不太了解NIO的同学,可以先看下相关系列文章。Netty的本质还是NIO。
Netty笔者之前也有学习过,主要是跟着这本书进行学习的,所以主要还是使用方面的,当然,学习完成之后,也从来没有使用到过,所以到目前为止也忘记的七七八八了。
记得有一次面试,面试官问我,你觉得Netty哪里好,支支吾吾的说了半天,就是觉得架构好,怎么个好法呢?具体细节又是蒙了。
针对这种典型的、通用的、高效的网络架构,个人觉得还是有必要好好学习一下的,不仅仅是使用层面的,而是更应该从源码角度,深入了解其架构设计,这样再跟别人聊的时候才有底气去说。
笔者使用的是Netty-4.1系列的代码,代码量还是非常大的。如果我们全部看完的话,深入每个细节去了解的话,很容易就陷入到代码细节的海洋中。所以我们一定要抓住主干部分,先去了解主要流程节点,然后再看其细节。
1.ServerBootStrap示例ServerBootStrap作为服务端,主要用于接收客户端连接请求,以及客户端发送的信息,并返回响应给客户端。主要示例代码如下:(代码主要来自于网络,做了一点修改)
public class HelloServer {
private static final int PORT = 18080;
public static void main(String[] args) {
// 设置boss线程池
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 设置work线程池
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
// 指定处理channel
.channel(NioServerSocketChannel.class)
// 设置属性值
.option(ChannelOption.SO_BACKLOG, 100)
// 指定server处理Handler
.handler(new LoggingHandler(LogLevel.INFO))
// 指定client处理Handler
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frame", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new HelloServerHandler());
}
});
// 绑定端口监听
ChannelFuture f = b.bind(PORT).sync();
// 监听服务器关闭监听
f.channel().closeFuture().sync();
// 可以简写为
/* b.bind(portNumber).sync().channel().closeFuture().sync(); */
} catch (Exception ex) {
ex.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class HelloServerHandler extends SimpleChannelInboundHandler {
/**
* 当连接建立时,执行的方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client" + ctx.channel().remoteAddress() + "connected");
ctx.writeAndFlush("welcome to our server\n");
super.channelActive(ctx);
}
/**
* 当失去连接时,执行的方法
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client" + ctx.channel().remoteAddress() + "disconnected");
super.channelActive(ctx);
}
/**
* 当获取到client发送过来的消息时,执行的方法
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("client " + ctx.channel().remoteAddress() + " say: " + msg);
ctx.writeAndFlush("msg received\n");
}
}
2.BootStrap示例
BootStrap作为客户端,主要用于发起对服务端的连接,发送信息到服务端和接收服务端的响应。代码示例如下:
public class HelloClient {
private static String HOST = "127.0.0.1";
private static int PORT = 18080;
public static void main(String[] args) {
// 设置线程池
EventLoopGroup group = new NioEventLoopGroup(1);
try {
Bootstrap b = new Bootstrap();
b.group(group)
// 设置执行channel
.channel(NioSocketChannel.class)
// 设置客户端处理Handler
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frame", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new HelloClientHandler());
}
});
// 连接服务端
Channel ch = b.connect(HOST, PORT).sync().channel();
// 控制台输入
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (; ; ) {
String line = in.readLine();
if (line == null) {
continue;
}
ch.writeAndFlush(line + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
public class HelloClientHandler extends SimpleChannelInboundHandler {
/**
* 当连接到server成功时,执行的方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client active");
super.channelActive(ctx);
}
/**
* 当与server的连接断开时,执行的方法
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client close");
super.channelInactive(ctx);
}
/**
* 接收到服务端的信息时执行的方法
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("receive server msg: " + msg);
}
}
以上客户端与服务端,演示了一个客户端带有输入框,可以不断发送消息到服务端,而服务端接收到消息后,打印到窗口并返回一个已接收到请求的响应。整个如下所示:
client:
// 连接到服务端
client active
receive server msg: welcome to our server
// 发送消息-1
see you see me
// 接收到响应-1
receive server msg: msg received
// 发送消息-2
see we together
// 接收到响应-2
receive server msg: msg received
server:
// 接收到客户端的连接
client/127.0.0.1:1768connected
// 接收到客户端信息-1
client /127.0.0.1:1768 say: see you see me
// 接收到客户端信息-2
client /127.0.0.1:1768 say: see we together
以上就是一个对话框的简单示例。
3.AbstractBootStrap分析通过分析ServerBootStrap和BootStrap的源码,可以看出
这两个类都继承了AbstractBootstrap,所以我们先来分析下该类的一些公共方法
public abstract class AbstractBootstrap implements Cloneable {
// 上面示例中的boss线程池和work线程池
volatile EventLoopGroup group;
// Channel工厂,用于创建channel对象
private volatile ChannelFactory, Object> options = new LinkedHashMap, Object> attrs = new ConcurrentHashMap, Object> childOptions = new LinkedHashMap, Object> childAttrs = new ConcurrentHashMap, Object>[] currentChildOptions;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
final Entry
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?