标签:
上一篇:【Netty4.X】Unity客户端与Netty服务器的网络通信(一)
如图所示,假如客户端分别发送两个数据包D1和D2给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4中情况:
修改上一篇【Netty4.X】Unity客户端与Netty服务器的网络通信(一)的ServerHandler类代码。在类中申明一个计数常量count,当每读到一条消息后,就count++,然后发送应答消息给客户端,代码如下:
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf)msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req,"UTF-8").substring(0, req.length - System.getProperty("line.separator").length()); count++; System.out.println("body"+body+";"+ ++count); String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)? new Date(System.currentTimeMillis()).toString():"BAD ORDER"; ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.writeAndFlush(resp); }
修改HttpClient的send()方法,当客户端与服务器链路建立成功之后,循环发送100条消息类代码。
public void Send() { if(client == null) { start (); } byte[] buffer = Encoding.UTF8.GetBytes("userName:"+userName.text+" password"+password.text); for(int i = 0;i < 100;i++) { client.Send(buffer); } }
控制台(服务器): +------------------------------------------------------------------+ 七月 07, 2016 8:09:35 下午 com.game.lll.net.HttpServer main 信息: 服务已启动... ad4ea569进来了 bodyuserName:aaa password:bbb ...此处省略36条 userName:aaa password:b;count:1 userName:aaa password:bbb ...此处省略36条 userName:aaa password;count:2 userName:aaa password:bbb ...此处省略22条 userName:aaa password:bbb;count:3 +------------------------------------------------------------------+服务端运行结果表明它只接收到三条消息,三条加起来一共是100条(如下图)。我们期待的是收到100条消息,每条消息都会包含一条“count:”.这说明发生了TCP粘包。
按照设计初衷,客户端应该收到100条AD ORDER消息,但实际上只收到了一条。
粘包的解决办法有很多,可以归纳如下。
在本案例中,我使用的是第2个解决办法在包尾增加回车换行符进行分割。
第一步:新建一个类ServerChannelHandler继承于ChannelInitializer,重点代码在26,27行,在原来的ServerHandler之前新增了两个解码器:LineBasedFrameDecoder和StringDecoder。代码如下
package com.game.lll.net; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class ServerChannelHandler extends ChannelInitializer<SocketChannel>{ public static void main(String[] args) throws Exception { int port = 8844; if(args!=null&&args.length>0) { try { port = Integer.valueOf(args[0]); } catch (Exception e) { // TODO: handle exception } } System.out.println(port); new HttpServer().bind(port); } @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new ServerHandler()); } }
第二部:修改原来的HttpServer类,修改代码如下。代码如下
package com.game.lll.net; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import io.netty.bootstrap.ServerBootstrap; 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; public class HttpServer { private static Log log = LogFactory.getLog(HttpServer.class); public void bind(int port) throws Exception { log.info("服务器已启动"); ////配置服务端的NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ServerChannelHandler()); } }).option(ChannelOption.SO_BACKLOG, 128) //最大客户端连接数为128 .childOption(ChannelOption.SO_KEEPALIVE, true); //绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { //优雅退出,释放线程池资源 workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }
第三步修改ServerHandler类。代码如下
package com.game.lll.net; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ServerHandler extends ChannelInboundHandlerAdapter{ private static Log log = LogFactory.getLog(ServerHandler.class); private int count = 0; @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { super.handlerAdded(ctx); System.out.println(ctx.channel().id()+"进来了"); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { super.handlerRemoved(ctx); System.out.println(ctx.channel().id()+"离开了"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String)msg; System.out.println("body"+body+";count:"+ ++count); String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?new Date(System.currentTimeMillis()).toString():"BAD ORDER"; currentTime = currentTime+System.getProperty("line.separator"); ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.writeAndFlush(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // TODO Auto-generated method stub ctx.close(); } }
直接看第33行,修改前后代码比较。修改前:
ByteBuf buf = (ByteBuf)msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req,"UTF-8");
修改后:
String body = (String)msg;
byte[] buffer = Encoding.UTF8.GetBytes("userName:"+userName.text+" password:"+password.text+"\r\n");在每一条消息尾巴后添加“\r\n’”
控制台(服务器): +------------------------------------------------------------------+ 8844 七月 07, 2016 8:37:58 下午 com.game.lll.net.HttpServer bind 信息: 服务器已启动 04d575ff进来了 bodyuserName:aaa password:bbb;count:1 此处省略很多条...... bodyuserName:aaa password:bbb;count:99 bodyuserName:aaa password:bbb;count:100 +------------------------------------------------------------------+
本章参考书籍
<<Netty权威指南(第2版)>>
【游戏开发】Netty TCP粘包/拆包问题的解决办法(二)
标签:
原文地址:http://blog.csdn.net/liulongling/article/details/51853083