TCP以流的方式进行数据传输,上层的应用协议为了对消息进行分区,往往采用下面4种方式:
(1)消息长度固定,累计读取到长度总和为定长LEN的报文后,就以为读取到了一个完整的消息;将计数器置位,重新开始读取下一个数据报;
(2)将回车换行符作为消息结束符,例如FTP协议,这种方式在文本协议中应用比较广泛;
(3)将特殊的分隔符作为消息的结束标志,回车换行符就是一种特殊的结束分隔符;
(4)通过在消息头中定义长度字段来标识消息的总长度。
我们先来看看通过DelimiterBasedFrameDecoder的使用,我们可以自动完成以分隔符作为码流结束标识的消息的解码;通过一个实例来演示,EchoServer接受到EchoClient的请求 消息后,将其打印出来,然后将原始消息返回给客户端,消息以”$_”作为分隔符。
1.EchoServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* @author
* @Description:
* @Date: Created in 10:47 2018/7/2
* @Modified By:
*/
public class EchoServer {
public static void main(String[] args) throws Exception {
int port = 9090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new EchoServer().bind(port);
}
public void bind(int port) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ByteBuf delimeter = Unpooled.copiedBuffer("$_".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimeter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2.EchoServerHandler.java
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* @author ${user}
* @Description:
* @Date: Created in 10:51 2018/7/2
* @Modified By:
*/
public class EchoServerHandler extends ChannelHandlerAdapter{
int counter = 0;
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String) msg;
System.out.println("this is "+ (++counter) + " times receive client : [ "+body+" ]");
body += "----------------------------.$_";
//System.out.println("body : "+body);
ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
ctx.writeAndFlush(echo);
}
}
3.EchoClient.java
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* @author ${user}
* @Description:
* @Date: Created in 10:53 2018/7/2
* @Modified By:
*/
public class EchoClient {
public void connect(int port,String host) throws Exception{
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<SocketChannel>() {
@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 future = bootstrap.connect(host,port).sync();
future.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 9090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new EchoClient().connect(port,"127.0.0.1");
}
}
4.EchoClientHandler.java
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* @author ${user}
* @Description:
* @Date: Created in 11:03 2018/7/2
* @Modified By:
*/
public class EchoClientHandler extends ChannelHandlerAdapter {
private int counter;
static final String ECHO_REQ = "Hi, i am client,welcome to netty.$_";
public EchoClientHandler(){
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i=0;i<10;i++){
ByteBuf buf = Unpooled.copiedBuffer(ECHO_REQ.getBytes());
ctx.writeAndFlush(buf);
}
}
@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 channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
测试结果:
1.服务端控制台结果
七月 02, 2018 11:16:02 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x09cd5cfd] REGISTERED
七月 02, 2018 11:16:02 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x09cd5cfd] BIND: 0.0.0.0/0.0.0.0:9090
七月 02, 2018 11:16:02 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x09cd5cfd, /0:0:0:0:0:0:0:0:9090] ACTIVE
七月 02, 2018 11:16:05 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x09cd5cfd, /0:0:0:0:0:0:0:0:9090] RECEIVED: [id: 0xda9d9898, /127.0.0.1:51862 => /127.0.0.1:9090]
this is 1 times receive client : [ Hi, i am client,welcome to netty. ]
this is 2 times receive client : [ Hi, i am client,welcome to netty. ]
this is 3 times receive client : [ Hi, i am client,welcome to netty. ]
this is 4 times receive client : [ Hi, i am client,welcome to netty. ]
this is 5 times receive client : [ Hi, i am client,welcome to netty. ]
this is 6 times receive client : [ Hi, i am client,welcome to netty. ]
this is 7 times receive client : [ Hi, i am client,welcome to netty. ]
this is 8 times receive client : [ Hi, i am client,welcome to netty. ]
this is 9 times receive client : [ Hi, i am client,welcome to netty. ]
this is 10 times receive client : [ Hi, i am client,welcome to netty. ]
2.客户端控制台
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 1
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 2
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 3
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 4
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 5
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 6
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 7
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 8
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 9
Now is : Hi, i am client,welcome to netty.—————————-.; the counter is : 10