一、服务端启动流程
1.服务端启动的最小代码:
public class NettyServer {
public static void main(String[] args) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
}
});
serverBootstrap.bind(8000);
}
}
上述代码:
首先创建了两个NioEventLoopGroup,这两个对象可以看作传统io线程中两大线程组,boosGroup表示监听端口,接收新连接的线程组,workerGroup表示处理每一个连接的数据读写的线程组,类似于工厂中的老板和员工,bossGroup类似于老板负责接活,workerGroup类似于员工来干活。
其次创建一个引导类ServerBootstrap,用来引导服务端的启动工作
然后通过.group(boosGroup,workerGroup)给引导类配置两大线程
指定服务端的IO模型为NIO模型
调用childHandler()方法,给引导类创建一个ChannelInitializer,是定义后续每个连接的数据读写
2.自动绑定递增端口:
上述示例中绑定了8000端口,但是,在一般情况下这个端口可能被占用,因此找一个没有被占用的端口号还是比较有概率性的,那我们如何将这件事情做到自动化呢?即我们只需要给一个起始的端口号,然后自动向上找一个可用的端口为止,以此类推。以下为实现代码:
serverBootstrap.bind(8000).addListener(new GenericFutureListener<F
uture<? super
Void>>() {
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
System.out.println("端口绑定成功!");
} else {
System.err.println("端口绑定失败!");
}
}
});
其中serverBootstrap方法为一个异步(不会等到端口绑定完成后再继续执行后续代码)的方法, 调用之后立即返回一个ChannelFuture,如此我们可以给这个对象绑定一个监听器GenericFutureListener,然后在监听器对象的operationComplete方法里监听端口是否绑定成功
接下来我们可以给一个其实端口号值1000开始网上找端口号,知道端口绑定成功,代码如下:
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);
}
}
});
}
上述代码示例中最关键的是在端口绑定失败之后,重新调用自身的方法把端口号+1
3.服务器启动的其他方法:
handler()方法:
serverBootstrap.handler(new ChannelInitializer<NioServerSocketChannel>
() {
protected void initChannel(NioServerSocketChannel ch) {
System.out.println("服务端启动中");
}
})
最上面的代码中的childHandler()方法用于用于处理新连接的读写处理逻辑,handler方法用于指定在服务器端启动过程中的一些逻辑
attr()方法:
serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServe
r")
给服务端的NioServerSocketChannel指定一些自定义属性,例如上述代码指定serverName,属性值为nettyServer
childAttr()方法:
serverBootstrap.childAttr(AttributeKey.newInstance("clientKey"), "clientV
alue")
给每一个连接指定自定义属性
option()方法:
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
系统用于临时存放已完成三次握手的请求队列的最大长度,如果连接建立比较频繁,服务器船舰新连接比较慢,可以适当的调大这个参数