github代码地址:https://github.com/gudepeng/gdpNetty
本教程使用的Netty版本为4.1.15.Final
一、Netty详解
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议的实现经验,这些协议包括FTP,SMTP,HTTP,各种二进制,文本协议,并经过相当精心设计的项目,最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
(来源于百度百科)
二、服务端编写
2.1代码展示
public class NettyServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
// 以("\n")为结尾分割的 解码器
.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new SimpleChannelInboundHandler<String>() {
@Override
public void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
// 收到消息直接打印输出
System.out.println(ctx.channel().remoteAddress() + "客戶端消息 :" + s);
// 返回客户端消息 - 我已经接收到了你的消息
ctx.writeAndFlush("收到你的消息\n");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "客户端发来链接");
ctx.writeAndFlush("欢迎链接\n");
super.channelActive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("发生错误");
cause.printStackTrace();
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端关闭");
super.channelInactive(ctx);
}
});
}
});
b.option(ChannelOption.SO_BACKLOG, 1024);
b.childOption(ChannelOption.SO_KEEPALIVE, true);
// 服务器绑定端口监听
ChannelFuture f = b.bind(8099).sync();
// 监听服务器关闭监听
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2.2详细讲解
ChannelInitializer类中可以设置具体的任务链:
可以添加编码器解码器,监听器和处理器等。
可以添加多个SimpleChannelInboundHandler类的处理器。
如果需要继续执行下个处理器,需要在channelRead0方法中的fireChannelActive()方法
SimpleChannelInboundHandler类中定义处理类:
1.channelActive方法:客户端第一次链接时执行
2.channelRead0方法:客户端发来信息执行
3.channelInactive方法:服务端关闭时执行
4.exceptionCaught方法:发生异常时执行
三、客户端编写
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.TCP_NODELAY, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
// 以("\n")为结尾分割的 解码器
.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new SimpleChannelInboundHandler<String>() {
@Override
public void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
// 收到消息直接打印输出
System.out.println("服务端消息 : " + s);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("发起服务端链接");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端关闭");
super.channelInactive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("发生错误");
cause.printStackTrace();
ctx.close();
}
});
}
});
// 连接服务端
Channel ch = b.connect("127.0.0.1", 8099).sync().channel();
// 控制台输入
BufferedReader in = new BufferedReader(new InputStreamReader(
System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
continue;
}
/*
* 向服务端发送在控制台输入的文本 并用"\r\n"结尾 之所以用\r\n结尾 是因为我们在handler中添加了
* DelimiterBasedFrameDecoder 帧解码。
* 这个解码器是一个根据\n符号位分隔符的解码器。所以每条消息的最后必须加上\n否则无法识别和解码
*/
ch.writeAndFlush(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}