后者可以自动完成对定长消息的解码。
首先是分隔符解码器在netty中的应用,服务端:
public class EchoServer {
public static void main(String[] args) {
int port = 6767;
if(args!=null&&args.length>0){
try {
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
//采用默认值
}
}
new EchoServer().blind(port);
}
public void blind(int port){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//本例中使用"$_"作为分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
/**
* 创建DelimiterBasedFrameDecoder对象,它有多个构造方法;
* 1024代表单条消息的最大长度,当达到该长度后任然没有找到分隔符,
* 就抛出ToolLongFrameException异常;
* 第二个参数就是分隔符缓冲对象
*/
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));
//将ByteBuf解码成字符串对象
ch.pipeline().addLast(new StringDecoder());
//EchoServerHandler接收到的msg消息就是解码后的字符串对象
ch.pipeline().addLast(new EchoServerHandler());
}
});
//绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
EchoServerHandler类:
public class EchoServerHandler extends ChannelHandlerAdapter{
int counter = 0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
/**
* DelimiterBasedFrameDecoder自动请求消息进行了解码
* 后续的ChannelHandler接收到的msg对象就是个完整的消息包
*/
String body = (String) msg;
System.out.println("This is "+ ++counter+"times receive client :【"+
body+"】");
/**
* 由于DelimiterBasedFrameDecoder过滤掉了分隔符,
* 所以返回给客户端是需要在请求消息尾部拼接分隔符"$_"
*/
body += "$_";
ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
ctx.writeAndFlush(echo);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端代码:
public class EchoClient {
public static void main(String[] args) {
int port = 6767;
if(args!=null&&args.length>0){
try {
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
//采用默认值
}
}
new EchoClient().connect(port,"127.0.0.1");
}
public void connect(int port,String host){
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
ch.pipeline().addLast(
new DelimiterBasedFrameDecoder(1024,delimiter)
);
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new EchoClientHandler());
}
});
//发起异步连接操作
ChannelFuture f = b.connect(host,port).sync();
//等待客户端链路关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();//优雅退出,释放NIO线程组
}
}
}
public class EchoClientHandler extends ChannelHandlerAdapter{
private int counter;
static final String ECHO_REQ = "Hi,LING. Welcome to Netty.$_";
public EchoClientHandler(){}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for(int i = 0;i<10;i++){
ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("This is "+ ++counter+"time receive server:["+msg+"]");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端运行结果:
即客户端发送了10条信息到服务端,并且服务端也接收到了客户端的10条信息,本例通过“$_”分割符,
用DelimiterBasedFrameDecoder对消息进行分割,没有出现粘包和拆包的现象。
定长解码器FixedLengthFrameDecoder在Netty中的应用:
服务端开发:
public class EchoServer1 {
public static void main(String[] args) {
int port = 6765;
if(args!=null&&args.length>0){
try {
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
//采用默认值
}
}
new EchoServer1().blind(port);
}
public void blind(int port){
//配置服务端的NIO线程组
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new FixedLengthFrameDecoder(20)); //定长解码器,截取20个字节长度的请求消息
p.addLast(new StringDecoder());
p.addLast(new EchoServerHandler1());
}
});
//绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
EchoServerHandler1类:
public class EchoServerHandler1 extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Receive client :["+msg+"]");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();//发生异常,关闭链路
}
}
1.在【运行】菜单中输入cmd命令,打开命令行窗口;
2.在命令行输入“telnet localhost 6765”连接服务端;(和服务端端口对应)
3.输入命令行内容(需要开启本地回显才能看到输入的内容,我这里一直输入aaaa)
服务端运行结果:
由于之前设置了截取20字节长度,所以这里有20个a
这就是FixedLengthFrameDecoder在netty中的应用。