netty自定义协议

java越来越多的用于服务器端的开发,少不了的要与客户端,服务器内部其他模块进行通信,netty帮我们实现了底层的通信,也实现了对一些常用协议的支持,比如http等,但有时候我们需要保证数据的及时性,安全性,比如游戏开发,及时性我们可能需要尽可能的控制协议包的大小,这时候我们可以自定义协议。

协议{
   协议头(header)
   协议数据(data)
}

总体将一个协议分为:协议头和协议数据

协议头:

header协议格式{
  tag             byte    协议头标志位
  encode  	  byte    数据编码格式
  encrypt  	  byte    加密类型
  extend1  	  byte    用于扩展协议
  extend2  	  byte
  sessionid  	  string length[32]  session
  length  	  int     协议数据(data)长度
  commandId       int     协议号
}

协议数据:就是我们需要的业务对象,后面写的编解码器就是帮助我们
业务对象->二进制数据 编码
二进制对象-> 业务对象 解码

header对象

/**
 * 请求和返回的头文件
 * 
 * @author zhaohui
 * 
 */
public class Header implements Cloneable {
	/** 数据编码格式。已定义:0:UTF-8,1:GBK,2:GB2312,3:ISO8859-1 **/
	private byte encode;
	/** 加密类型。0表示不加密 **/
	private byte encrypt;
	/** 用于扩展协议。暂未定义任何值 **/
	private byte extend1;
	/** 用于扩展协议。暂未定义任何值 **/
	private byte extend2;
	/** 会话ID **/
	private String sessionid;
	/** 数据包长 **/
	private int length;
	/** 命令 **/
	private int commandId;

	@Override
	public Header clone() {
		try {
			return (Header) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	public Header() {

	}

	public Header(String sessionid) {
		this.encode = 0;
		this.encrypt = 0;
		this.sessionid = sessionid;
	}

	public Header(byte encode, byte encrypt, byte extend1, byte extend2,
			String sessionid, int length, int commandId) {
		this.encode = encode;
		this.encrypt = encrypt;
		this.extend1 = extend1;
		this.extend2 = extend2;
		this.sessionid = sessionid;
		this.length = length;
		this.commandId = commandId;
	}
        get/set省略

	@Override
	public String toString() {
		return "header [encode=" + encode + ",encrypt=" + encrypt + ",extend1="
				+ extend1 + ",extend2=" + extend2 + ",sessionid=" + sessionid
				+ ",length=" + length + ",commandId=" + commandId + "]";
	}

}

消息对象:

/**
 * 消息
 * 
 * @author zhaohui
 * 
 */
public class Message {
	/** 头消息 **/
	private Header header;
	/** 数据 **/
	private Object data;

	public Message() {

	}

	public Message(Header header) {
		this.header = header;
	}

	public Message(Header header, Object data) {
		this.header = header;
		this.data = data;
	}
        get/set省略
}

header解码器:

public class HeaderDecoder extends FrameDecoder {

	/**头文件长度**/
	public static final int HEAD_LENGHT = 45;
	/** 包头标志 **/
	public static final byte PACKAGE_TAG = 0x01;

	@Override
	protected Object decode(ChannelHandlerContext ctx, Channel channel,
			ChannelBuffer buffer) throws Exception {
		if (buffer.readableBytes() < HEAD_LENGHT) {
			return null;
		}
		buffer.markReaderIndex();
		byte tag = buffer.readByte();
		if (tag != PACKAGE_TAG) {
			throw new CorruptedFrameException("非法协议包");
		}
		byte encode = buffer.readByte();
		byte encrypt = buffer.readByte();
		byte extend1 = buffer.readByte();
		byte extend2 = buffer.readByte();
		byte sessionByte[] = new byte[32];
		buffer.readBytes(sessionByte);
		String sessionid = new String(sessionByte);
		int length = buffer.readInt();
		int commandId = buffer.readInt();

		if (buffer.readableBytes() < length) {
			buffer.resetReaderIndex();
			return null;
		}

		Header header = new Header(encode, encrypt, extend1, extend2,
				sessionid, length, commandId);
		Message message = new Message(header, buffer.readBytes(length));

		return message;
	}

}

header编码器:

public class HeaderEncoder extends OneToOneEncoder {

	@Override
	protected Object encode(ChannelHandlerContext ctx, Channel channel,
			Object msg) throws Exception {
		if (!(msg instanceof Message)) {
			return msg;
		}

		Message message = (Message) msg;
		ChannelBuffer buffer = (ChannelBuffer) message.getData();
		Header header = message.getHeader();

		ChannelBuffer allBuffer = ChannelBuffers.dynamicBuffer();
		allBuffer.writeByte(HeaderDecoder.PACKAGE_TAG);
		allBuffer.writeByte(header.getEncode());
		allBuffer.writeByte(header.getEncrypt());
		allBuffer.writeByte(header.getExtend1());
		allBuffer.writeByte(header.getExtend2());
		allBuffer.writeBytes(header.getSessionid().getBytes());
		allBuffer.writeInt(buffer.readableBytes());
		allBuffer.writeInt(header.getCommandId());
		allBuffer.writeBytes(buffer);
		return allBuffer;
	}
}

抽象解码器:

/**
 * 解码器
 *
 * 将二进制数据转换成需要的业务逻辑对象
 * @author zhaohui
 *
 */
public abstract class Decoder extends OneToOneDecoder {

	@Override
	protected Object decode(ChannelHandlerContext ctx, Channel channel,
			Object msg) throws Exception {
		if (!(msg instanceof Message)) {
			return msg;
		}
		Message message = (Message) msg;
		Header header = message.getHeader();
		transformData(header.getCommandId(), message);
		return message;
	}

	/**
	 * 将二进制数据转换成逻辑对象
	 * 
	 * @param logicObj  
	 * 					逻辑对象
	 * @param message   
	 * 					请求对象
	 * @throws Exception
	 */
	protected abstract void transformData(int commandId, Message message)
			throws Exception;

}

抽象编码器:

public abstract class Encoder extends OneToOneEncoder {

	@Override
	protected Object encode(ChannelHandlerContext ctx, Channel channel,
			Object msg) throws Exception {
		Message message = (Message) msg;
		transformData(message);
		return msg;
	}

	/**
	 * 将逻辑对象转换成二进制数据
	 * @param message
	 * @throws Exception
	 */
	protected abstract void transformData(Message message) throws Exception;

}

抽象方法中提供的transformData就是将我们的业务对象和二进制数据进行互转,让业务层更专注于逻辑。
常用的数据格式:protobuf(推荐),json等.更多…
好了,下面就是根据具体的数据格式进行扩展了。