-
IDEA引入netty:
或者引入依赖(推荐)
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
服务端代码(解释以注释的形式体现):
设置步骤:线程模型、IO模型,IO业务处理逻辑,绑定端口
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.AttributeKey;
public class NettyServer {
public static final AttributeKey<String> SERVER_NAME = AttributeKey.newInstance("serverName");
public static final AttributeKey<String> CLIENT_KEY = AttributeKey.newInstance("clientKey");
public static void main(String[] args) {
//绑定的端口
final int port=9999;
//监听端口,创建新连接的线程组(parentGroup: 接活的线程组)
NioEventLoopGroup bossGroup=new NioEventLoopGroup();
//处理每一条连接的数据读写的线程组(childGroup: 干活的线程组)
NioEventLoopGroup workerGroup=new NioEventLoopGroup();
//引导类,引导进行服务端的启动工作
ServerBootstrap serverBootstrap = new ServerBootstrap()
//一:服务端相关配置:
//1 配置两大线程组
.group(bossGroup, workerGroup)
//2 指定服务端所用的IO模型:NioServerSocketChannel.class为NIO,OioServerSocketChannel.class为BIO
// 参数NioServerSocketChannel是对NIO类型的连接的抽象,可以和BIO编程模型中的ServerSocket概念对应上
.channel(NioServerSocketChannel.class)
//3 给服务端的NioServerSocketChannel指定一些自定义属性,通过channel.attr()取出这个属性(基本用不到)
// 注意把key使用静态变量存,否则会出现IllegalArgumentException: 'xxx' is already in use netty
.attr(SERVER_NAME, "nettyServer")
//4 指定在服务端启动过程中的一些逻辑(基本用不到)
.handler(new ChannelInitializer<NioServerSocketChannel>(){
@Override
protected void initChannel(NioServerSocketChannel ch) throws Exception {
System.out.println(".attr()绑定的数据为:"+ch.attr(SERVER_NAME).get());
System.out.println("服务端启动中");
}
})
//5 给服务端channel设置属性
// 系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数
.option(ChannelOption.SO_BACKLOG, 1024)
//二:客户端连接相关配置:
//6 给每一个代表客户端的NioSocketChannel连接指定自定义属性,然后后续可以通过channel.attr()取出该属性
// 注意把key使用静态变量存,否则会出现IllegalArgumentException: 'xxx' is already in use netty
.childAttr(CLIENT_KEY, "clientValue")
//7 定义后续每条连接的数据读写,业务处理逻辑
.childHandler(new ChannelInitializer<NioSocketChannel>() {//泛型参数NioSocketChannel就是Netty对NIO类型的连接的抽象,可以和BIO编程模型中的Socket概念对应上
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
System.out.println(".childAttr()绑定的数据为:"+ch.attr(CLIENT_KEY).get());
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
//8 给每条连接设置一些TCP底层相关的属性
//是否开启TCP底层心跳机制,true为开启
.childOption(ChannelOption.SO_KEEPALIVE, true)
//是否开始Nagle算法,true表示关闭,false表示开启,通俗地说,如果要求高实时性,有数据发送时就马上发送,就关闭,如果需要减少发送次数减少网络交互,就开启。
.childOption(ChannelOption.TCP_NODELAY, true);
//9 递归:在本地绑定一个端口,如果绑定失败,者往上找端口号,直到端口绑定成功为止
bindPort(serverBootstrap,port);
}
private static void bindPort(final ServerBootstrap serverBootstrap,final int port) {
ChannelFuture channelFuture = serverBootstrap.bind(port);
//10 给ChannelFuture添加一个监听器GenericFutureListener,然后在GenericFutureListener的operationComplete方法里面监听端口是否绑定成功
channelFuture.addListener(future -> {
if (future.isSuccess()) {
System.out.println("端口绑定成功!端口为:"+port);
} else {
System.err.println("端口绑定失败!");
//如果绑定失败,者往上找端口号,直到端口绑定成功为止
bindPort(serverBootstrap,port+1);
}
});
}
}
总结:
- 创建一个引导类,然后指定线程模型,IO模型,连接读写处理逻辑,绑定端口之后,服务端就启动起来了。
- bind 方法是异步的,通过这个异步机制来实现端口递增绑定。
- 可以给服务端 Channel 或者客户端 Channel 设置属性值并获取,设置底层 TCP 参数等。