黏包&拆包處理
netty提供了很多frame的編解碼器 本次解析LengthFieldBaseFrameDecoder
黏包出現 客戶端發了10條數句,server端有時候 只打印一次,10條數據被融合了
public class ServerHandlerChai extends SimpleChannelInboundHandler<ByteBuf> {
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
int count = 0;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
String content = new String(bytes , Charset.forName("UTF-8"));
System.out.println("收到客戶端的消息======"+content);
System.out.println("收到客戶端的消息 讀取的次數======"+ (++count));
ByteBuf byteBuf = Unpooled.copiedBuffer("這是服務器送給客戶端的一段話" , Charset.forName("UTF-8"));
ctx.writeAndFlush(byteBuf);
}
}
public class SimpleClientHandlerChai extends SimpleChannelInboundHandler<ByteBuf> {
int count = 0;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
String content = new String(bytes , Charset.forName("UTF-8"));
System.out.println(String.format("消息內容-> %s", content));
System.out.println(String.format("消息次數-> %s", ++count));
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
for(int i=0;i<=10;i++){
ByteBuf byteBuf = Unpooled.copiedBuffer("客戶端來信================= "+i+"/r/n" , Charset.forName("UTF-8"));
ctx.writeAndFlush(byteBuf);
}
}
}
如何破解拆包黏包,自定義協議解析器
重點看下編碼器和解碼器
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup bossGroupChai = new NioEventLoopGroup();
NioEventLoopGroup workGroupChai = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap().
group(bossGroupChai , workGroupChai).
channel(NioServerSocketChannel.class).
childHandler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder" , new DeppyuDecoder());
pipeline.addLast("encoder" , new DeppyuEncoder());
pipeline.addLast("serverHandler", new ServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(5566)).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroupChai.shutdownGracefully();
workGroupChai.shutdownGracefully();
}
}
}
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup eventGroup = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap().group(eventGroup).
channel(NioSocketChannel.class).
handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder" , new DeppyuDecoder());
pipeline.addLast("encoder" , new DeppyuEncoder());
pipeline.addLast("clientHandler", new ClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect("localhost",5566).sync();
channelFuture.channel().closeFuture().sync();
}finally {
eventGroup.shutdownGracefully();
}
}
}
public class ServerHandler extends SimpleChannelInboundHandler<DeppyuProtocol> {
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext ctx, DeppyuProtocol msg) throws Exception {
String content = new String(msg.getBytes());
System.out.println(String.format("收到服務器的消息內容-> %s", content));
String answer = "做個好人吧";
byte[] contentBytes = answer.getBytes();
int length = contentBytes.length;
DeppyuProtocol deppyu = new DeppyuProtocol();
deppyu.setLength(length);
deppyu.setBytes(contentBytes);
ctx.writeAndFlush(deppyu);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println(String.format("[channelActive]-> [%s] 上線", channel.remoteAddress()));
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
// System.out.println(String.format("[channelInactive]-> [%s] 下線", channel.remoteAddress()));
channelGroup.writeAndFlush(String.format("[channelInactive]-> [%s] 下線 \n", channel.remoteAddress()));
super.channelInactive(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
if(channelGroup.size()>=1){
channelGroup.writeAndFlush(String.format("[handlerAdded]-> [%s] 加入 \n", channel.remoteAddress()));
}
channelGroup.add(channel);
super.handlerAdded(ctx);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush(String.format("[服務端]-> [%s] 斷開連接 \n", channel.remoteAddress()));
super.handlerRemoved(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
public class ClientHandler extends SimpleChannelInboundHandler<DeppyuProtocol> {
int count = 0;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, DeppyuProtocol msg) {
String content = new String(msg.getBytes());
System.out.println(String.format("收到服務器的消息內容-> %s", content));
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
for(int i=0;i<=10;i++){
String content = "客戶端來信==="+i;
int length = content.getBytes().length;
byte[] bytes = content.getBytes();
DeppyuProtocol deppyu = new DeppyuProtocol();
deppyu.setLength(length);
deppyu.setBytes(bytes);
ctx.writeAndFlush(deppyu);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("異常======"+cause.getMessage());
super.exceptionCaught(ctx, cause);
}
}
public class DeppyuEncoder extends MessageToByteEncoder<DeppyuProtocol> {
@Override
public void encode(ChannelHandlerContext ctx, DeppyuProtocol msg, ByteBuf out){
System.out.println("encode===========");
out.writeInt(msg.getLength());
out.writeBytes(msg.getBytes());
}
}
public class DeppyuDecoder extends ReplayingDecoder<Void> {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out){
System.out.println("decode===========");
int length = in.readInt();
byte[] content = new byte[length];
in.readBytes(content);
DeppyuProtocol deppyu = new DeppyuProtocol();
deppyu.setLength(length);
deppyu.setBytes(content);
out.add(deppyu);
}
}
關於replayingDecoer爲啥可以認爲數據全部到齊,具體的可以看鏈接的文章
大體和它的javadoc描述類似,設置mark點,如果數據沒到齊 拋異常,後續再處理
ReplayingDecoder傳遞一個專門的ByteBuf實現,當緩衝區中沒有足夠的數據時,這個實現會拋出某種類型的錯誤。在上面的IntegerHeaderFrameDecoder中,您只是假設在調用buf.readInt()時,緩衝區中有4個或更多字節。如果緩衝區中確實有4個字節,它將像您期望的那樣返回整數報頭。否則,將引發錯誤並將控制返回到ReplayingDecoder。如果ReplayingDecoder捕捉到錯誤,那麼它會將緩衝區的readerIndex倒回“初始”位置(即緩衝區的開始),並在緩衝區接收到更多數據時再次調用decode(..)方法。
請注意,ReplayingDecoder總是拋出相同的緩存錯誤實例,以避免每次拋出時創建新錯誤並填充其堆棧跟蹤的開銷。