netty粘包(一)消息定长 实践
服务端:
package com.jds.test.pack1;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Created by sunyuming on 18/8/1.
*/
public class Server4 {
public static void main(String[] args) {
//boss线程监听端口,worker线程负责数据读写
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try{
//辅助启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//设置线程池
bootstrap.group(boss,worker);
//设置socket工厂
bootstrap.channel(NioServerSocketChannel.class);
//设置管道工厂
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//获取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//定长解码类
pipeline.addLast(new FixedLengthFrameDecoder(14));
//字符串解码类
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
//处理类
pipeline.addLast(new ServerHandler4());
}
});
//绑定端口
ChannelFuture future = bootstrap.bind(8866).sync();
System.out.println("server start ...... ");
//等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//优雅退出,释放线程池资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
package com.jds.test.pack1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.InetAddress;
/**
* Created by sunyuming on 18/8/1.
*/
class ServerHandler4 extends SimpleChannelInboundHandler<String> {
//用于记录次数
private int count = 0;
//读取客户端发送的数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("RESPONSE--------"+msg+";"+" @ "+ ++count);
ctx.writeAndFlush(msg);
}
//新客户端接入
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
//客户端断开
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭通道
ctx.channel().close();
//打印异常
cause.printStackTrace();
}
}
客户端:
package com.jds.test.pack1;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
/**
* Created by sunyuming on 18/8/1.
*/
public class Client4 {
public static void main(String[] args) {
//worker负责读写数据
EventLoopGroup worker = new NioEventLoopGroup();
try {
//辅助启动类
Bootstrap bootstrap = new Bootstrap();
//设置线程池
bootstrap.group(worker);
//设置socket工厂
bootstrap.channel(NioSocketChannel.class);
//设置管道
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//获取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//定长解码类
pipeline.addLast(new FixedLengthFrameDecoder(14));
//字符串编码类
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
//处理类
pipeline.addLast(new ClientHandler4());
}
});
//发起异步连接操作
ChannelFuture futrue = bootstrap.connect(new InetSocketAddress("127.0.0.1",8866)).sync();
//等待客户端链路关闭
futrue.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅的退出,释放NIO线程组
worker.shutdownGracefully();
}
}
}
package com.jds.test.pack1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* Created by sunyuming on 18/8/1.
*/
class ClientHandler4 extends SimpleChannelInboundHandler<String> {
//接受服务端发来的消息
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("server response : "+msg);
}
//与服务器建立连接
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//给服务器发消息
String s = "--";
//发送50次消息
for (int i = 0; i < 5; i++) {
ctx.channel().writeAndFlush("I am client,"+s);
}
}
//与服务器断开连接
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭管道
ctx.channel().close();
//打印异常信息
cause.printStackTrace();
}
}
(一) 都14
服务端输出:
server start ......
channelActive
RESPONSE--------I am client,--; @ 1
RESPONSE--------I am client,--; @ 2
RESPONSE--------I am client,--; @ 3
RESPONSE--------I am client,--; @ 4
RESPONSE--------I am client,--; @ 5
客户端输出:
server response : I am client,--
server response : I am client,--
server response : I am client,--
server response : I am client,--
server response : I am client,--
(二)服务端13,客户端14
服务端:
server start ......
channelActive
RESPONSE--------I am client,-; @ 1
RESPONSE---------I am client,; @ 2
RESPONSE----------I am client; @ 3
RESPONSE--------,--I am clien; @ 4
RESPONSE--------t,--I am clie; @ 5
总向客户端发行字节13*5
客户端:
server response : I am client,--
server response : I am client,--
server response : I am client,--
server response : I am client,--
客户端收到13*5=65,65/14=4.6
(三)都13
服务端:
channelActive
RESPONSE--------I am client,-; @ 1
RESPONSE---------I am client,; @ 2
RESPONSE----------I am client; @ 3
RESPONSE--------,--I am clien; @ 4
RESPONSE--------t,--I am clie; @ 5
客户端:
server response : I am client,-
server response : -I am client,
server response : --I am client
server response : ,--I am clien
server response : t,--I am clie
(四)服务端去除,客户端13
服务端:
server start ......
channelActive
RESPONSE--------I am client,--I am client,--I am client,--I am client,--I am client,--; @ 1
总向客户端发行字节14*5=70
客户端:
server response : I am client,-
server response : -I am client,
server response : --I am client
server response : ,--I am clien
server response : t,--I am clie
客户端收到14*5=70,70/13=5.4
(五)都去除
服务端:
server start ......
channelActive
RESPONSE--------I am client,--I am client,--I am client,--I am client,--I am client,--; @ 1
客户端:
server response : I am client,--I am client,--I am client,--I am client,--I am client,--
参考:
https://blog.csdn.net/baiye_xing/article/details/73189191
【Netty入门】解决TCP粘包/分包的实例
================================================================================
补充:
源码中为单向发送,服务端只有StringDecoder,客户端只有StringEncoder,本例测试双向发包拆包,故两端都要加上编码器和解码器
8.22之所以未在exceptionCaught中报错,是因为write和writeAndFlush为出站操作,且为异步,不会立即抛出异常,捕获异常方式有二:
1)
ChannelFuture channelFuture = ctx.writeAndFlush(newMsg);
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if(!future.isSuccess()) {
future.cause().printStackTrace();
future.channel().close();
}
}
});
可见抛出异常:
java.lang.UnsupportedOperationException: unsupported message type: String
2)


8.20,再补充:
serverhandler的channelRead0函数中直接将msg
ctx.writeAndFlush(msg);
write是个异步操作,而SimpleChannelInboundHandler的channelRead在其channelRead0后会释放msg:
ReferenceCountUtil.release(msg);
故该句应改为:
StringBuilder stringBuilder = new StringBuilder().append(msg);
String newMsg = stringBuilder.toString();
ctx.writeAndFlush(newMsg);
小结:
如果使用SimpleChannelInboundHandler,注意msg的复制;
如果使用ChannelInboundHandlerAdapter,注意msg的释放和转发;
2019.12.5 注意,此时的msg已经是java对象,所以如果要往下传递ServerHandler5,不需要复制
浙公网安备 33010602011771号