承接上一章,接下来开始进入Netty入门,本文所采用的Netty版本号是5.0,这个请大家注意下。
还是上一个场景,一个简单的客户端和服务端直接发送字符串的程序,如果使用Netty框架如何展示呢?废话不多说,直接上代码。
服务端:
package com.dlb.note.server;
importio.netty.bootstrap.ServerBootstrap;
importio.netty.buffer.ByteBuf;
importio.netty.buffer.Unpooled;
importio.netty.channel.*;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.SocketChannel;
importio.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 功能:时间服务器
* 版本:1.0
* 日期:2016年12月8日14:58:06
* 作者:馟苏
*/
public classTimeServer {
/**
* 主函数
*/
public static voidmain(String []args) {
// 配置服务端的NIO线程池
EventLoopGroup bossGroup =newNioEventLoopGroup();
EventLoopGroup workerGroup =newNioEventLoopGroup();
try{
ServerBootstrap serverBootstrap =newServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
// 当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(newChildChannelHandler());
// 绑定端口,同步等待成功
ChannelFuture future = serverBootstrap.bind(8888).sync();
System.out.println("服务器在8888端口监听hello");
// 等待服务端监听端口关闭
future.channel().closeFuture().sync();
System.out.println("服务器关闭bye");
}catch(Exception e) {
e.printStackTrace();
}finally{
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
classChildChannelHandlerextendsChannelInitializer {
@Override
protected voidinitChannel(SocketChannel socketChannel)throwsException {
socketChannel.pipeline().addLast(newTimerServerHandler());
}
}
classTimerServerHandlerextendsChannelHandlerAdapter {
// 可读
@Override
public voidchannelRead(ChannelHandlerContext ctx,Object msg)throwsException {
// 读数据
ByteBuf buf = (ByteBuf) msg;
byte[] req =new byte[buf.readableBytes()];
buf.readBytes(req);
String body =newString(req,"UTF-8");
System.out.println("receive:"+ body);
// 写数据
ByteBuf res = Unpooled.copiedBuffer("hello,client!".getBytes());
ctx.write(res);
ctx.flush();
}
// 连接
@Override
public voidchannelActive(ChannelHandlerContext ctx)throwsException {
System.out.println("client come,ip:"+ ctx.channel().remoteAddress());
}
// 关闭
@Override
public voidchannelInactive(ChannelHandlerContext ctx)throwsException {
System.out.println("client close,ip:"+ ctx.channel().remoteAddress());
ctx.close();
}
// 异常
@Override
public voidexceptionCaught(ChannelHandlerContext ctx,Throwable cause)throwsException {
System.out.println(cause.toString());
ctx.close();
}
}
现在来分析一下:首先服务端做了两个线程池,一个是boss线程池,一个worker线程,这是Netty框架给我们提供的。并且在使用完毕后,可以优雅的关闭。然后做一个服务器启动引导serverBootStrap,我们可以设置TCP连接的属性,Netty给我们提供了很多属性。接下来,注册管道,设置处理器,绑定端口监听。
其中比较重要的是处理器,我们可以这么考虑,Netty为我们提供了一个管道,管道中存在许多个处理器。一个客户端链接对应着一个管道。这样我们就可以把处理逻辑写在处理器中。在处理器中我们主要关心这么几个方法,可读:channelRead、链接到来:channelActive、链接断开:channelInactive、异常:exceptionCaught。并且在链接关闭后,直接通过ctx.close();可以将关联的通道等都关闭,并且释放资源。
客户端:
package com.dlb.note.client;
importio.netty.bootstrap.Bootstrap;
importio.netty.buffer.ByteBuf;
importio.netty.buffer.Unpooled;
importio.netty.channel.*;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.nio.NioSocketChannel;
/**
* 功能:支持tcp粘包/拆包的时间客户端
* 版本:1.0
* 日期:2016/12/9 15:40
* 作者:馟苏
*/
public classTimeClient {
/**
* main函数
*@paramargs
*/
public static voidmain(String []args) {
EventLoopGroup group =newNioEventLoopGroup();
try{
Bootstrap bootstrap =newBootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(newChannelInitializer() {
protected voidinitChannel(Channel channel)throwsException {
channel.pipeline().addLast(newMyHandler());
}
});
// 等待客户端链接成功
ChannelFuture future = bootstrap.connect("localhost",8888).sync();
System.out.println("客户端链接成功!");
// 等待客户端链接关闭
future.channel().closeFuture().sync();
}catch(Exception e) {
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
}
classMyHandlerextendsChannelHandlerAdapter {
@Override
public voidchannelActive(ChannelHandlerContext ctx)throwsException {
for(inti =0;i <1000;i++) {
ByteBuf msg = Unpooled.copiedBuffer(("你好啊,服务器,我是revoid啊!$_".getBytes()));
ctx.writeAndFlush(msg);
}
ctx.close();
super.channelActive(ctx);
}
}
客户端也是非常简单的,首先创建一个线程池,然后注册管道,处理器,在链接服务器成功后,给服务器发送消息。
通过上述介绍,我们可以发现,通过Netty框架编写一个简单的客户端和服务端通信程序,只需要寥寥几行就可以实现。但是仍然还有一个问题没有解决,那就是没有解决,TCP的粘包和分包问题,这个问题,将会在下一章介绍。