Netty游戏前后台通信协议分析

给大家推荐一个网站:http://blog.csdn.net/youxijishu/article/details/44938751

通信协议分析

一,Socket传输方式

         TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。

UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。

由于TCP无消息保护边界, 需要在消息接收端处理消息边界问题。也就是为什么我们以前使用UDP没有此问题。反而使用TCP后,出现少包的现象。

二,粘包分析

     上面说了原理,但可能有人使用TCP通信会出现多包/少包,而一些人不会。那么我们具体分析一下,少包,多包的情况。
正常情况,发送及时每消息发送,接收也不繁忙,及时处理掉消息。像
UDP一样.

         

发送粘包,多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包这种情况和客户端处理繁忙,接收缓存区积压,用户一次从接收缓存区多个数据包的接收端处理一样。

         

发送粘包或接收缓存区积压,但用户缓冲区大于接收缓存区数据包总大小。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。

         

发送粘包或接收缓存区积压, 用户缓存区不是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,同时也需要考虑数据包不完整。

         

为了解决以上引起接收的包不完整的情况,我们必须知道每个包的大小。这样才能从数据流中取出完整的数据。实现思路如下:

         协议分成三个部分:

                   1)一个包的总数据流长度,即一次传输的字节数(占一个int位,4个字节)

                   2)数据体的长度,即用于逻辑计算的数据的长度。(占一个int位,4个字节)

                   3)数据体字节。

         一个完整的协议格式如下:

                   总长度_数据体长度_数据体。(总长度包括它自己本身占的字节数)

三,Java代码部分实现

          1)接收到第一个数据流之后,选判断这个数据流的长度是否大于4个字节,如果小于4个字节,则保存此次接收到的数据,下次再接收到数据时,把新接收的数据字节添加到上次接收的字节后面。

          2)如果第一个数据流大于4个字节,读取这4个字节,得到整个数据包的长度n,判断剩下的字节是否大于等于(n-4)个字节。如果是,则从剩下的字节中取出(n - 4) 个字节,这就是数据包的内容。如果还有剩下的字节,则执行(1)

     netty实现:netty对这些过程进行了封装,使用起来非常方便:

 

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

 

public class MessageDecoder extends LengthFieldBasedFrameDecoder{

         public MessageDecoder(int maxFrameLength, int lengthFieldOffset,

                            int lengthFieldLength) {

                   super(maxFrameLength, lengthFieldOffset, lengthFieldLength);

                   // TODO Auto-generated constructor stub

         }

         protected Object decode(ChannelHandlerContext ctx,ByteBuf in)throws Exception{

                   System.out.println("可读字节:" + in.readableBytes());

                   ByteBuf frame = (ByteBuf) super.decode(ctx, in);

                   if(frame == null){

                            return null;

                   }

                   int len = frame.readInt();

                   len = frame.readInt();

                   byte[] bys = new byte[len];

                   frame.readBytes(bys);

                   String result = new String(bys,"utf8");

                   System.out.println("Game接收到的json:" +result);

 

                   //此处返回的信息就是在public void channelRead(ChannelHandlerContext ctx, Object msg)使用的msg

                   return result;

         }

 

}

 

把这个类添加到netty的处理链中就可以了:

public void start() {

                   this.initServerInfo();

                   int PORT = ConfigManager.getLocalConfig().getPort();

                   ServerBootstrap b = new ServerBootstrap();

                   b.group(bossGroup, workerGroup);

                   b.channel(NioServerSocketChannel.class);

                   b.option(ChannelOption.SO_BACKLOG, 2048);

                   b.childHandler(new ChannelInitializer<SocketChannel>() {

                            @Override

                            public void initChannel(SocketChannel ch) throws Exception {

                                     ChannelPipeline pipeline = ch.pipeline();

                                     // 设置读取超时时间,30s

                                     pipeline.addLast(new ReadTimeoutHandler(TIME_OUT));

                                     // 设置写超时时间:30s

                                     pipeline.addLast(new WriteTimeoutHandler(TIME_OUT));

                                     // 设置空闲时间:60s

                                     pipeline.addLast(new IdleStateHandler(TIME_OUT, TIME_OUT,

                                                        TIME_OUT));

                                     // pipeline.addLast(new LineBasedFrameDecoder(2048));

                                     // pipeline.addLast(new StringDecoder(Charset.forName("utf8")));

                                     pipeline.addLast(new MessageDecoder(1024 * 1024, 4, 4));

                                     pipeline.addLast(new MessageEncoder());

                                     pipeline.addLast(new SystemHandle());

                                     pipeline.addLast(new MainProcessHandle());

                            }

                   });

 

                   try {

 

                            ChannelFuture f = b.bind(PORT).sync();

                           

                            System.out.println("====服务器启动成功====");

                            f.channel().closeFuture().sync();

 

                   } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            CommonLog.ERR("服务器初始化错误" + e.getMessage() + "\n" + e.getCause());

                   } finally {

                            bossGroup.shutdownGracefully();

                            workerGroup.shutdownGracefully();

                   }

         }

原文:http://www.youxijishu.com/blog/myBlog/2/6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值