前言
Netty是什么,简而言之,Netty就是一个高性能、事件驱动的异步的非堵塞的IO(NIO)框架,本是JBoss做的一个Jar包,用于快速开发高性能、高可靠性的网络服务器和客户端程序,通俗来讲,就是一个处理Socket的工具。更多讨论详见知乎https://www.zhihu.com/question/24322387。接下来我们从简单的入门程序开始了解一下Netty。
服务端
package com.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* netty5服务端
* @author -师德帅-
*
*/
public class Server {
public static void main(String[] args) {
//服务类
ServerBootstrap bootstrap = new ServerBootstrap();
//boss和worker,两个线程池
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
//设置线程池
bootstrap.group(boss, worker);
//设置socket工厂
bootstrap.channel(NioServerSocketChannel.class);
//设置管道工厂
bootstrap.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());//解码器(包中自带)
ch.pipeline().addLast(new StringEncoder());//编码器(包中自带)
ch.pipeline().addLast(new ServerHandler());//处理消息的Handler(需创建)
}
});
//设置参数,TCP参数
bootstrap.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接
bootstrap.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送
//绑定端口
ChannelFuture future = bootstrap.bind(10101);
System.out.println("服务端启动。");
//等待服务端关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
对应的ServerHandler类
package com.server; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 服务端消息处理 * @author -师德帅- * */ public class ServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); //回写数据,收到客户端发来的消息之后,回写给客户端 //这两种方式的效果相同ctx.channel().writeAndFlush("你好,我是服务端回复的消息。"); ctx.writeAndFlush("你好,我是服务端回复的消息。"); } /** * 新客户端接入 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } /** * 客户端断开 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelInactive"); } /** * 异常 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } }
客户端
package com.client; import java.io.BufferedReader; import java.io.InputStreamReader; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * netty5的客户端 * @author -师德帅- * */ public class Client { public static void main(String[] args) { //服务类 Bootstrap bootstrap = new Bootstrap(); //worker线程池 EventLoopGroup worker = new NioEventLoopGroup(); try { //设置线程池,他不需要监听客户端连接,所以只需要设置worker就可以了 bootstrap.group(worker); //设置socket工厂 bootstrap.channel(NioSocketChannel.class); //设置管道 bootstrap.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new StringDecoder());//同服务端 ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new ClientHandler()); } }); //连接服务端 ChannelFuture connect = bootstrap.connect("127.0.0.1", 10101); //获取控制台录入 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); while(true){ System.out.println("请输入:"); String msg = bufferedReader.readLine();//单行读取控制台输入 connect.channel().writeAndFlush(msg);//将控制台输入发送到服务端 } } catch (Exception e) { e.printStackTrace(); } finally{ worker.shutdownGracefully(); } } }
同理,有一个ClientHandler
package com.client; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 客户端消息处理 * @author -师德帅- * */ public class ClientHandler extends SimpleChannelInboundHandler<String> { @Override//客户端收到消息之后需要调用的方法 protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("客户端收到消息:"+msg); } }
测试
启动服务端与客户端,在客户端的控制台输入一行字符,enter,这时,服务器端会接收到该客户端发来的消息并回写数据给客户端。
总结
这个入门的小程序虽然不是很难,每步的作用在注释中也有体现,具体为什么要做这些步骤,我们就要分析netty 的源码了。在实际运用中,还要注意很多地方,比如,粘包问题,socket攻击等,相比之下,netty的可取之处还是非常多的,具体它的优点可以参考前言中给出的链接,详细介绍了netty框架的种种优点。