Java游戏服务器开发之十五--使用StringMessage封装消息

之前我们的消息都是直接通过使用String,然后通过netty的默认编解码器StringDecoder、StringEncoder来进行传输,而我们需要的不仅仅是文本内容,还需要扩展一些其他的东西(消息号、状态码之类的)。

消息传输可以看看这篇游戏中tcp消息使用byte字节数组传输
所以就想到用一个message进行封装,同时自定义自己的编解码器。
备注:这篇内容修改的内容比较多,或许有些凌乱,同时在后面这篇修改的内容或许会更改,大家可以查看版本记录进行详细对比。
添加
IMessage.java                            消息的公共接口,这里暂时没用到
MessageDecoder.java                        消息编码器,发送消息时使用        
MessageEncoder.java                        消息编码器,接收消息时
StringMessage.java                        String类型的消息
修改
INetworkConsumer.java                    修改consume(String message, Channel channel)为consume(StringMessage message, Channel channel)
NetworkConsumer.java                    修改messageId从message中获取
SessionManager.java                        添加一个sendMessage(Session session, StringMessage stringMessage) 方法
TcpMessageStringHandler.java            将其中的泛型类改成StringMessage
TcpServerStringInitializer.java            
TcpStringClientHandlerTest.java            将其中的泛型类改成StringMessage    
TcpStringClientTest.java
TestFirstHandler.java                    修改AbstractHandler的泛型类为StringMessage    
UserService.java                        修改接收类为StringMessage
UserServiceImpl.java                    修改接收类为StringMessage


1.StringMessage
    首先我们的消息是StringMessage,然后里面的内容可以使用json、xml,这边是使用json格式
    看下里面的字段  int messageId; int statusCode;  int length; String body;
    
    里面就是消息一些比较重要的内容,
        messageId是根据不同的值,进行路由区分的
        statusC内容的长ode主要是返回内容是告诉客户端消息的状态,成功为1,其他不同的错误使用不同的错误码
        length是度,内容的长度是不可控制的,所以使用一个长度进行定义
        body是具体的内容
2.MessageEncoder
    消息编码器,我们发送信息的时候是直接使用StringMessage,但是netty不知道我们发送的数据内容,所以要通过这个类进行转换,转化成系统知道的类型ByteBuf
    让这个类继承MessageToByteEncoder,重写encode方法,依次写入 messageId; statusCode; length; body;
3.MessageDecoder        
    消息解码器,同上,在消息接受的时候,通过这个类的转化,我们可以得到StringMessage
    让这个类继承LengthFieldBasedFrameDecoder,重写decode方法,然后读出messageId; statusCode; length; body;即可
    
具体的代码就是:
        StringMessage

/*
 * Copyright (C), 2015-2018
 * FileName: StringMessage
 * Author:   zhao
 * Date:     2018/7/12 10:55
 * Description: 发送过来的请求
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.message;

import com.lizhaoblog.base.util.GsonUtil;

/**
 * 〈一句话功能简述〉<br>
 * 〈String类型的请求〉
 *
 * @author zhao
 * @date 2018/7/12 10:55
 * @since 1.0.1
 */
public class StringMessage {

  /**
   * 消息号
   */
  private int messageId;
  /**
   * 状态码
   */
  private int statusCode;
  /**
   * 内容长度
   */
  private int length;
  /**
   * 具体内容
   */
  private String body;

  public StringMessage() {
  }

  public static StringMessage create(int messageId) {
    StringMessage stringMessage = new StringMessage();
    stringMessage.setMessageId(messageId);
    return stringMessage;
  }

  public static StringMessage create(String origin) {
    StringMessage stringMessage = GsonUtil.fromJson(origin, StringMessage.class);
    return stringMessage;
  }

  public static StringMessage create(int length, int messageId, int statusCode, String content) {
    return new StringMessage(length, messageId, statusCode, content);
  }

  private StringMessage(int length, int messageId, int statusCode, String body) {
    this.length = length;
    this.messageId = messageId;
    this.statusCode = statusCode;
    this.body = body;
  }

  public int getLength() {
    return length;
  }

  public void setLength(int length) {
    this.length = length;
  }

  public int getMessageId() {
    return messageId;
  }

  public void setMessageId(int messageId) {
    this.messageId = messageId;
  }

  public int getStatusCode() {
    return statusCode;
  }

  public void setStatusCode(int statusCode) {
    this.statusCode = statusCode;
  }

  public String getBody() {
    return body;
  }

  public void setBody(String body) {
    this.body = body;
  }

  @Override
  public String toString() {
    return "StringMessage{" + "messageId=" + messageId + ", statusCode=" + statusCode + ", length=" + length
            + ", body='" + body + '\'' + '}';
  }
}


        MessageEncoder

/*
 * Copyright (C), 2015-2018
 * FileName: MessageEncoder
 * Author:   zhao
 * Date:     2018/7/16 14:58
 * Description: 消息编码器
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.message.codec;

import com.lizhaoblog.base.constant.ConstantValue;
import com.lizhaoblog.base.message.StringMessage;

import java.nio.charset.Charset;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * 〈一句话功能简述〉<br>
 * 〈消息编码器〉
 *
 * @author zhao
 * @date 2018/7/16 14:58
 * @since 1.0.1
 */
public class MessageEncoder extends MessageToByteEncoder<StringMessage> {

  @Override
  protected void encode(ChannelHandlerContext ctx, StringMessage msg, ByteBuf out) throws Exception {
    if (null == msg) {
      throw new Exception("msg is null");
    }

    String body = msg.getBody();
    byte[] bodyBytes = body.getBytes(Charset.forName(ConstantValue.PROJECT_CHARSET));
    out.writeInt(msg.getMessageId());
    out.writeInt(msg.getStatusCode());
    out.writeInt(bodyBytes.length);
    out.writeBytes(bodyBytes);
  }

}


        MessageDecoder

/*
 * Copyright (C), 2015-2018
 * FileName: MessageDecoder
 * Author:   zhao
 * Date:     2018/7/16 15:05
 * Description: 消息解码器
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.message.codec;

import com.lizhaoblog.base.constant.ConstantValue;
import com.lizhaoblog.base.message.StringMessage;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

/**
 * 〈一句话功能简述〉<br>
 * 〈消息解码器〉
 *
 * @author zhao
 * @date 2018/7/16 15:05
 * @since 1.0.1
 */
public class MessageDecoder extends LengthFieldBasedFrameDecoder {

  //判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 int+int+int = 4+4+4 = 12
  private static final int HEADER_SIZE = 12;

  private int messageId;
  private int statusCode;
  private int length;

  private String body;

  /**
   * @param maxFrameLength      解码时,处理每个帧数据的最大长度
   * @param lengthFieldOffset   该帧数据中,存放该帧数据的长度的数据的起始位置
   * @param lengthFieldLength   记录该帧数据长度的字段本身的长度
   * @param lengthAdjustment    修改帧数据长度字段中定义的值,可以为负数
   * @param initialBytesToStrip 解析的时候需要跳过的字节数
   * @param failFast            为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
   */
  public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
          int initialBytesToStrip, boolean failFast) {
    super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
  }

  @Override
  protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
    if (in == null) {
      return null;
    }
    if (in.readableBytes() < HEADER_SIZE) {
      throw new Exception("可读信息段比头部信息都小");
    }

    //注意在读的过程中,readIndex的指针也在移动
    messageId = in.readInt();
    statusCode = in.readInt();
    length = in.readInt();

    if (in.readableBytes() < length) {
      throw new Exception("body获取长度" + length + ",实际长度没有达到");
    }
    ByteBuf buf = in.readBytes(length);
    byte[] req = new byte[buf.readableBytes()];
    buf.readBytes(req);
    body = new String(req, ConstantValue.PROJECT_CHARSET);

    //    CustomMsg customMsg = new CustomMsg(type, flag, length, body);
    StringMessage stringMessage = StringMessage.create(length, messageId, statusCode, body);
    return stringMessage;
  }

}

上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231524

猜你喜欢

转载自blog.csdn.net/cmqwan/article/details/81082578