Netty是由JBOSS提供的一个java开源框架。java
Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。算法
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty能够确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。spring
Netty至关于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。编程
“快速”和“简单”并不用产生维护性或性能上的问题。Netty是一个吸取了多种协议(包括FTP、SMTP、HTTP等各类二进制文本协议)的实现经验,并通过至关精心设计的项目。bootstrap
最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性服务器
本文讲解SpringBoot如何使用Netty服务端和客户端的简单案例以及相关参数解释网络
1、Netty服务端框架
一、导入依赖异步
< dependency>socket
< groupId>org.springframework.boot< /groupId>
< artifactId>spring-boot-starter< /artifactId>
< /dependency>
< dependency>
< groupId>org.projectlombok< /groupId>
< artifactId>lombok< /artifactId>
< /dependency>
< dependency>
< groupId>io.netty< /groupId>
< artifactId>netty-all< /artifactId>
< version>4.1.36.Final< /version>
< /dependency>
二、编写Netty服务端处理器
/**
@author Gjing
netty服务端处理器 **/ @Slf4j public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
*/ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("Channel active......");
}
/** * 客户端发消息会触发
*/ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("服务器收到消息: {}", msg.toString());
ctx.write("你也好哦");
ctx.flush();
}
/**
*/ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } 三、编写Netty服务端初始化器
/**
/**
public void start(InetSocketAddress socketAddress) {
//new 一个主线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//new 一个工做线程组
EventLoopGroup workGroup = new NioEventLoopGroup(200);
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer())
.localAddress(socketAddress)
//设置队列大小
.option(ChannelOption.SO_BACKLOG, 1024)
// 两小时内没有数据的通讯时,TCP会自动发送一个活动探测数据报文
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,开始接收进来的链接
try {
ChannelFuture future = bootstrap.bind(socketAddress).sync();
log.info("服务器启动开始监听端口: {}", socketAddress.getPort());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭主线程组
bossGroup.shutdownGracefully();
//关闭工做线程组
workGroup.shutdownGracefully();
}
} }
五、启动类
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args) ; //启动服务端
NettyServer nettyServer = new NettyServer();
nettyServer.start(new InetSocketAddress("127.0.0.1", 8090));
}
} 六、启动结果
< dependency>
< groupId>org.springframework.boot< /groupId>
< artifactId>spring-boot-starter< /artifactId>
< /dependency>
< dependency>
< groupId>org.projectlombok< /groupId>
< artifactId>lombok< /artifactId>
< /dependency>
< dependency>
< groupId>io.netty< /groupId>
< artifactId>netty-all< /artifactId>
< version>4.1.36.Final< /version>
< /dependency>
二、编写客户端处理器
/**
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端Active .....");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("客户端收到消息: {}", msg.toString());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
三、编写客户端初始化器
/**
@author Gjing
客户端初始化器
**/ public class NettyClientInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("decoder", new StringDecoder());
socketChannel.pipeline().addLast("encoder", new StringEncoder());
socketChannel.pipeline().addLast(new NettyClientHandler());
}
}
四、编写客户端
/**
**/ @Component
@Slf4j
public class NettyClient {
public void start() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap()
.group(group)
//该参数的做用就是禁止使用Nagle算法,使用于小数据即时传输
.option(ChannelOption.TCP_NODELAY, true)
.channel(NioSocketChannel.class)
.handler(new NettyClientInitializer());
try {
ChannelFuture future = bootstrap.connect("127.0.0.1", 8090).sync();
log.info("客户端成功....");
//发送消息
future.channel().writeAndFlush("你好啊");
// 等待链接被关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
}
五、启动类
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
//启动netty客户端
NettyClient nettyClient = new NettyClient();
nettyClient.start();
}
} 六、启动结果
客户端
3、ChannelOption参数详解 一、ChannelOption.SO_BACKLOG
ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,函数listen(int socketfd,int backlog)用来初始化服务端可链接队列,服务端处理客户端链接请求是顺序处理的,因此同一时间只能处理一个客户端链接,多个客户端来的时候,服务端将不能处理的客户端链接请求放在队列中等待处理,backlog参数指定了队列的大小
二、ChannelOption.SO_REUSEADDR
ChanneOption.SO_REUSEADDR对应于套接字选项中的SO_REUSEADDR,这个参数表示容许重复使用本地地址和端口,好比,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就能够解决问题,该参数容许共用该端口,这个在服务器程序中比较常使用,好比某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能容许其余进程使用,并且程序死掉之后,内核一须要必定的时间才可以释放此端口,不设置SO_REUSEADDR就没法正常使用该端口。
三、ChannelOption.SO_KEEPALIVE
Channeloption.SO_KEEPALIVE参数对应于套接字选项中的SO_KEEPALIVE,该参数用于设置TCP链接,当设置该选项之后,链接会测试连接的状态,这个选项用于可能长时间没有数据交流的链接。当设置该选项之后,若是在两小时内没有数据的通讯时,TCP会自动发送一个活动探测数据报文
四、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF
ChannelOption.SO_SNDBUF参数对应于套接字选项中的SO_SNDBUF,ChannelOption.SO_RCVBUF参数对应于套接字选项中的SO_RCVBUF这两个参数用于操做接收缓冲区和发送缓冲区的大小,接收缓冲区用于保存网络协议站内收到的数据,直到应用程序读取成功,发送缓冲区用于保存发送数据,直到发送成功。
五、ChannelOption.SO_LINGER
ChannelOption.SO_LINGER参数对应于套接字选项中的SO_LINGER,Linux内核默认的处理方式是当用户调用close()方法的时候,函数返回,在可能的状况下,尽可能发送数据,不必定保证会发生剩余的数据,形成了数据的不肯定性,使用SO_LINGER能够阻塞close()的调用时间,直到数据彻底发送
六、ChannelOption.TCP_NODELAY
ChannelOption.TCP_NODELAY参数对应于套接字选项中的TCP_NODELAY,该参数的使用与Nagle算法有关,Nagle算法是将小的数据包组装为更大的帧而后进行发送,而不是输入一次发送一次,所以在数据包不足的时候会等待其余数据的到了,组装成大的数据包进行发送,虽然该方式有效提升网络的有效负载,可是却形成了延时,而该参数的做用就是禁止使用Nagle算法,使用于小数据即时传输,于TCP_NODELAY相对应的是TCP_CORK,该选项是须要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。
七、IP_TOS
IP参数,设置IP头部的Type-of-Service字段,用于描述IP包的优先级和QoS选项。
八、ALLOW_HALF_CLOSURE
Netty参数,一个链接的远端关闭时本地端是否关闭,默认值为False。值为False时,链接自动关闭;为True时,触发ChannelInboundHandler的userEventTriggered()方法,事件为ChannelInputShutdownEvent。
4、Netty的future.channel().closeFuture().sync();到底有什么用?
主线程执行到这里就 wait 子线程结束,子线程才是真正监听和接受请求的,closeFuture()是开启了一个channel的监听器,负责监听channel是否关闭的状态,若是监听到channel关闭了,子线程才会释放,syncUninterruptibly()让主线程同步等待子线程结果