# 解决tcp拆包,粘包问题 * 方案一:消息定长 * 方案二:在包尾部加上特殊字符进行分割 * 方案三:将消息分为消息头,消息体,类似与自定义协议
此处示例在尾部添加特殊字符解决拆包粘包问题
在 ServerBootstrap的childHandler方法里面(initChannel)添加
// 设置特殊分隔符 ByteBuf buf = Unpooled.copiedBuffer("$end$".getBytes()); sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf)); // 设置字符串形式的解码 sc.pipeline().addLast(new StringDecoder());
算了,直接看代码吧:
Client.java
package zzq.neety02;
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.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;
public class Client {
public static void main(String[] args) throws InterruptedException {
// 创建线程组
NioEventLoopGroup group = new NioEventLoopGroup();
// 创建辅助工具类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group) //绑定线程组
.channel(NioSocketChannel.class) // 指定 NIO 模式
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
// 设置特殊分隔符
ByteBuf buf = Unpooled.copiedBuffer("$end$".getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
// 设置字符串形式的解码
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ClientHandler()); // 在这里配置数据接收处理的方法(类)
}
});
// 绑定连接地址
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",8888).sync();
// 向服务端发送消息
channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer("你好,一加一对于多少$end$".getBytes()));
channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer("你好,1234$end$".getBytes()));
channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer("你好,5678$end$".getBytes()));
channelFuture.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
ClientHandler.java
package zzq.neety02;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println( cause.toString() );
System.out.println("出错了...");
ctx.close();
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println( "注册成功..." );
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println( "激活成功..." );
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String resp = (String) msg;
System.out.println( "收到了消息:" + msg );
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("我读完了...");
}
}
Server.java
package zzq.neety02;
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.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 java.nio.Buffer;
public class Server {
public static void main(String[] args) throws InterruptedException {
//1 创建线两个程组
//一个是用于处理服务器端接收客户端连接的
//一个是进行网络通信的(网络读写的)
NioEventLoopGroup pGroup = new NioEventLoopGroup();
NioEventLoopGroup cGroup = new NioEventLoopGroup();
//2.创建辅助工具类,用于服务器通信的一系列配置
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(pGroup,cGroup) // 绑定两个线程组
.channel(NioServerSocketChannel.class) // 指定NIO模式
.option(ChannelOption.SO_KEEPALIVE , true) // 设置保持连接
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
// 设置特殊分隔符
ByteBuf buf = Unpooled.copiedBuffer("$end$".getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
// 设置字符串形式的解码
sc.pipeline().addLast(new StringDecoder());
// 3. 在这里配置具体的数据处理方法
sc.pipeline().addLast(new ServerHandler());
}
});
//4 进行绑定
ChannelFuture cf1 = bootstrap.bind(8888).sync();
//ChannelFuture cf2 = b.bind(8764).sync();
//5 等待关闭
cf1.channel().closeFuture().sync();
//cf2.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
ServerHandler.java
package zzq.neety02;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.util.Base64;
import java.util.Date;
public class ServerHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 出现异常
System.out.println( cause.toString() );
ctx.close();
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("netty is register...");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("netty is active...");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String)msg;
System.out.println("客户端发来消息:"+ request );
String resp = "服务端响应:收到了你的消息==>" + new Date() + "$end$";
ctx.writeAndFlush( Unpooled.copiedBuffer(resp.getBytes()) );
System.out.println("返回数据:" + resp );
}
}