您的位置:首页 > 其它

Netty学习笔记:2

2015-07-18 13:06 381 查看
1、TCP粘包/拆包

概念是什么?

TCP是个“流”协议,所谓流,就是没有界限的一串数据。

而一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个包封装成一个大的数据包发送,这就是所谓TCP,粘包/拆包

流可以被抽象理解成一条和,那粘包就是两个数据在河流里面贴在一起,而拆包就是一个数据在河流里面被分成两截。

2、成因和解决策略

成因感觉很好理解,想象一下就行。

解决方案直接上代码,是对上一篇文章所载代码的改进。

package com.ftrend.netty.release2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeServerHandler extends ChannelHandlerAdapter {
private int counter;

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8").substring(0, req.length - System.getProperty("line.separator").length());
System.out.println("The time server recevie order : " + body + " ; the counter is : " + ++counter);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis())
.toString() : "BAD ORDER";
currentTime = currentTime + System.getProperty("line.separator");
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.writeAndFlush(resp);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}

package com.ftrend.netty.release2;

import java.util.logging.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeCilentHandler extends ChannelHandlerAdapter {

private static final Logger logger = Logger.getLogger(TimeCilentHandler.class.getName());

private int counter;

private byte[] req;

public TimeCilentHandler(){
req = ("QUERY TIME ORDER" +  System.getProperty("line.separator")).getBytes();
}

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf message = null;
for(int i = 0; i < 100; i++){
message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req,"UTF-8");
System.out.println("Now is : " + body + " ;the counter is : " + ++counter);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}

public static void main(String[] args) {

}

}


这个例子对比于上一章,只改变了这俩Handler文件,在客户端那个类,25行到28行,循环发送了100条消息,每发送一条就刷新一次,按照初衷应该打印100条查询时间命令的请求消息。

可是结果是这样的。

The time server recevie order : QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORD ; the counter is : 1
The time server recevie order :
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER ; the counter is : 2


也就是说只发送了两次。

这说明发生了粘包

关于TCP粘包/拆包导致的半包读写问题,Netty默认提供了,多种编解码器用于处理半包。

下面是解决了TCP粘包的TimeServer和TimeClient

package com.ftrend.netty.release3;

import io.netty.bootstrap.ServerBootstrap;
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.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeServer {
public void bind(int port) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChildChannelHandler());
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
arg0.pipeline().addLast(new StringDecoder());
arg0.pipeline().addLast(new TimeServerHandler());
}
}

public static void main(String[] args) throws Exception {
new TimeServer().bind(39998);
}
}
package com.ftrend.netty.release3;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeServerHandler extends ChannelHandlerAdapter {
private int counter;

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("The time server recevie order : " + body + " ; the counter is : " + ++counter);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis())
.toString() : "BAD ORDER";
currentTime = currentTime + System.getProperty("line.separator");
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.writeAndFlush(resp);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}

package com.ftrend.netty.release3;

import io.netty.bootstrap.Bootstrap;
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.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeClient {
public void connect(int port, String host) throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeCilentHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
}finally{
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new TimeClient().connect(39998, "127.0.0.1");
}
}

package com.ftrend.netty.release3;

import java.util.logging.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeCilentHandler extends ChannelHandlerAdapter {
private static final Logger logger = Logger.getLogger(TimeCilentHandler.class.getName());
private int counter;
private byte[] req;

public TimeCilentHandler(){
req = ("QUERY TIME ORDER" +  System.getProperty("line.separator")).getBytes();
}

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf message = null;
for(int i = 0; i < 100; i++){
message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("Now is : " + body + " ;the counter is : " + ++counter);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}


其实就是加了编码解码的代码,然后在Handler中删除编码解码的代码
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
arg0.pipeline().addLast(new StringDecoder());

运行后的结果也大大
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 1
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 2
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 3
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 4
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 5
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 6
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 7
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 8
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 9
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 10
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 11
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 12
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 13
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 14
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 15
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 16
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 17
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 18
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 19
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 20
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 21
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 22
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 23
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 24
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 25
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 26
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 27
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 28
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 29
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 30
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 31
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 32
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 33
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 34
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 35
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 36
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 37
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 38
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 39
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 40
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 41
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 42
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 43
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 44
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 45
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 46
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 47
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 48
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 49
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 50
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 51
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 52
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 53
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 54
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 55
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 56
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 57
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 58
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 59
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 60
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 61
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 62
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 63
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 64
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 65
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 66
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 67
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 68
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 69
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 70
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 71
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 72
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 73
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 74
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 75
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 76
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 77
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 78
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 79
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 80
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 81
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 82
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 83
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 84
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 85
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 86
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 87
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 88
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 89
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 90
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 91
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 92
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 93
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 94
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 95
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 96
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 97
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 98
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 99
Now is : Sat Jul 18 14:25:18 CST 2015 ;the counter is : 100


这说明LineBasedFrameDecoder和StringDecoder解决了粘包拆包问题。

最后解释一下为什么

LineBasedFrameDecoder就是以换行符作为结束标志的解码器,

而StringDecoder仅仅是简单的编码成String

还有两种解码器

分隔符解码器:DelimierBasedFrameDecoder

定长解码器:FixedLengthFrameDecoder
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: