项目里有个聊天的功能,之前一直用mina,一直有一个bug没解决,那就是断包、少包、和粘包的问题。
谷歌度娘了很多文章,可惜都以失败而告终。
这个问题很奇怪,就算做了处理,还是不行。
以下情况会出现那个bug
第一:当客户端与服务器保持连接时,如果长时间未发送过长的较长的消息(心跳一直有)的情况下,突然发送一条较长的json字符串,就会出现这个问题
第二:当客户端频繁与服务器进行数据传送,也会出现这个问题。
原字符串:
{"message":{"content":"gghh ","msgid":"1413260146146","userlogo":"http:\/\/192.168.1."userlogo":"http:\/\/192.168.1.21:8888\/upload\/image\/201435\/e0feff917266411290f1.jpg","nickname":"申兵兵","msgtype":0,"receiverUid":"8",21:8888\/upload\/image\/201435\/e0feff917266411290f1.jpg","nickname":"申兵兵","msgtype":0,"receiverUid":"8","userid":"7","sendUid":"7","timel":0,"ctime":"2014-10-14 12:15:46"},"sendUid":"7","msgid":"1413260146146","cmd":"order_sendmessage","receiverUid":"8"}
接收到的字符串:
{"message":{"content":"gghh ","msgid":"1413260146146","userid":"7","sendUid":"7","timel":0,"ctime":"2014-10-14 12:15:46"},"sendUid":"7","msgid":"1413260146146","cmd":"order_sendmessage","receiverUid":"8"}
"userlogo":"http:\/\/192.168.1.21:8888\/upload\/image\/201435\/e0feff917266411290f1.jpg","nickname":"申兵兵","msgtype":0,"receiverUid":"8",
你没有看错,将会以两次来接收,第一次接收头和尾,第二次接收了中间的,真是莫名其妙。
后来改成了Netty,上手还是很快的。
添加 netty-all-5.0.0.Alpha1.jar 就可以了
首先启动服务,注意,一定要在线程里启动,不过加入你的整个项目只有一个简单的main方法的话,可以无视
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new HttpHelloWorldServerInitializer());
Channel ch = b.bind(SocketManage.WORDPORT).sync().channel();
ch.closeFuture().sync();
}catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
HttpHelloWorldServerInitializer.class
public class HttpHelloWorldServerInitializer extends ChannelInitializer {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("timeout", new IdleStateHandler(60, 15, 0));//定时
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder(Charset.forName("utf-8")));
pipeline.addLast("encoder", new StringEncoder(Charset.forName("utf-8")));
pipeline.addLast("heartbeat", new MyHandler());//心跳
pipeline.addLast("handler",new HttpHelloWorldServerHandler());//业务
}
}
MyHandler.class 就是拦截心跳和发送心跳的
public class MyHandler extends SimpleChannelInboundHandler {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
// System.out.println("--- Reader Idle ---");
// ctx.close();
} else if (e.state() == IdleState.WRITER_IDLE) {
// System.out.println("--- Write Idle ---");
ctx.writeAndFlush("#\n");
}
}
}
@Override
protected void messageReceived(ChannelHandlerContext ctx, String str)
throws Exception {
if(str.equals("#")){
return;
}
ctx.fireChannelRead(str);//传递到下一层,因为在HttpHelloWorldServerInitializer中注册了两个handelr,首先会进这里
}
}
HttpHelloWorldServerHandler.class 与上一个MyHandler是一样的
public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler {
@Override
protected void messageReceived(ChannelHandlerContext ctx, String str)
throws Exception {
//str就是消息啦,在这里就可以进行业务逻辑了
}
}
大功告成!!注意发消息和接收消息都要以\n结尾。