所谓的粘包/拆包,用一个例子来说明就是:
加入客户端向服务端发送1000条数据,如果不加以处理的话,那么服务端接收的数据可能就是如图所示了:
数据要么几段粘在了一起,要么一段数据被拆成了几段,这肯定会造成很大的影响。
而解决后的所接收的正确数据该如下所示:
简单讲了一下粘包/拆包是什么样的问题,详细解释可见csdn博客
http://blog.youkuaiyun.com/binghuazh/article/details/4222516
客户端代码:
package com.netty.dealpacket;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
/**
* @author Chalmers 2016年2月24日 下午2:35:39
*/
public class Client {
public static void main(String[] args) throws UnknownHostException,
IOException {
Socket socket = new Socket("127.0.0.1", 9090);
String message = "hello";
byte[] bytes = message.getBytes();
// 设置空间大小为一个存储了长度的int型数据(长度)加上转换后的byte数组
ByteBuffer buffer = ByteBuffer.allocate(4 + bytes.length);
// 将长度存入
buffer.putInt(bytes.length);
// 将数据存入
buffer.put(bytes);
// 转换成字节数组
byte[] array = buffer.array();
// 向服务端发送1000次
for (int i = 0; i < 1000; i++) {
socket.getOutputStream().write(array);
}
// 关闭
socket.close();
}
}
处理问题代码:
package com.netty.dealpacket;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
/**
* @author Chalmers 2016年2月24日 下午2:23:49
*/
public class MyDecoder extends FrameDecoder {
@Override
protected Object decode(ChannelHandlerContext chc, Channel channel,
ChannelBuffer buffer) throws Exception {
// 如果buffer中的可读字节大于4个(即除了长度以外还有数据,因为长度可能是为0的)
if (buffer.readableBytes() > 4) {
// 标记,指向当前指针位置,读取数据时使用
buffer.markReaderIndex();
// 取得长度
int len = buffer.readInt();
// 如果剩余可读字节小于长度的话,则表明发生了拆包现象,那么不对它进行处理
if (buffer.readableBytes() < len) {
// 重置标记
buffer.resetReaderIndex();
// 返回null,表示等待
return null;
}
// 对数据进行处理
byte[] bytes = new byte[len];
buffer.readBytes(bytes);
// 将数据返回到ServerHandler中进行处理
return new String(bytes);
}
return null;
}
}
package com.netty.dealpacket;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* @author Chalmers 2016年2月24日 下午2:22:41
*/
public class ServerHandler extends SimpleChannelHandler {
int count = 1;
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
// 对从MyDecoder中传递过来的数据进行处理
System.out.println((String) e.getMessage() + " " + count);
count++;
}
}
服务端代码:
package com.netty.dealpacket;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
* @author Chalmers 2016年2月24日 下午2:21:33
*/
public class Server {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boss,
worker));
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new MyDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ServerHandler());
return pipeline;
}
});
serverBootstrap.bind(new InetSocketAddress(9090));
System.out.println("start...");
}
}