Netty 学习笔记2 服务端
maven 依赖
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
</dependency>
基本使用
- 创建引导类 ServerBootstrap 对象
ServerBootstrap serverBootstrap = new ServerBootstrap();
- 使用 group 方法配置事件循环组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
serverBootstrap.group(bossGroup, workerGroup);
// 第一个 bossGroup 父事件循环组,主要职责为:接受新连接线程,创建新连接
// workerGroup 为子事件循环组,主要职责为:负责读取数据的线程,主要用于读取数据以及业务逻辑处理
当然也可以设置成一个事件循环组,即该组既负责接收连接又负责读取数据,但不推荐
EventLoopGroup loopGroup = new NioEventLoopGroup();
serverBootstrap.group(loopGroup);
- 设置 channel 类型
// 设置成非阻塞IO模型
serverBootstrap.channel(NioServerSocketChannel.class);
当然也可以设置成其他渠道类型,比如阻塞型
serverBootstrap.channel(OioServerSocketChannel.class);
// 但 OioServerSocketChannel 已被标注成过期类,不建议使用
还有其他一些选择,如 EpollServerSocketChannel,KQueueServerSocketChannel
- 为 workerGroup 配置处理器链;
这一步很关键
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline = socketChannel.pipeline(); // 管道
channelPipeline.addLast(new Xxx());
}
});
// 也可以给 bossGroup 配置处理器
// 用于指定在服务端启动过程中的一些逻辑
serverBootstrap.handler(new LoggingHandler(LogLevel.DEBUG));
- 设置参数
// option() 方法 ,给服务端channel设置一些属性
// 表示系统用于临时存放已完成三次握手的请求的队列的最大长度,
// 如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
// childOption() 方法 ,可以给每条连接设置一些TCP底层相关的属性
// ChannelOption.SO_KEEPALIVE表示是否开启TCP底层心跳机制,true为开启
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 表示是否开启Nagle算法,true表示关闭,false表示开启,
// 通俗地说,如果要求高实时性,有数据发送时就马上发送,就关闭,如果需要减少发送次数减少网络交互,就开启。
serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
- 绑定端口号
serverBootstrap.bind(51001);
// 或者这样,写法不同而已
serverBootstrap.localAddress(51001);
serverBootstrap.bind();
由于 bind 是一个异步方法,会立即返回,所以一般我们会加上一个监听器
serverBootstrap.bind(51001).addListener(new GenericFutureListener<Future<? super Void>>() {
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
System.out.println("端口 51001 绑定成功!");
} else {
System.err.println("端口 51001 绑定失败!");
}
}
});
智能化绑定端口
private static void bind(final ServerBootstrap serverBootstrap, final int port) {
serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
System.out.println("端口[" + port + "]绑定成功!");
} else {
System.err.println("端口[" + port + "]绑定失败!");
bind(serverBootstrap, port + 1);
}
}
});
}
- 其他一些方法
// attr()方法可以给服务端的 channel,也就是 NioServerSocketChannel 指定一些自定义属性,
// 然后我们可以通过 channel.attr() 取出这个属性
serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServer");
// childAttr 可以给每一条连接指定自定义属性,然后后续我们可以通过 channel.attr() 取出该属性。
serverBootstrap.childAttr(AttributeKey.newInstance("clientKey"), "clientValue");
完整示例
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import morning.cat.netty.server.handle.HelloHttpServerHandle;
public class ServerMain {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline = socketChannel.pipeline(); // 管道
channelPipeline.addLast(new HttpServerCodec());
// ...
}
});
ChannelFuture channelFuture = serverBootstrap.bind(51001).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}