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等.更多... http://www.oschina.net/question/12_91812
好了,下面就是根据具体的数据格式进行扩展了。
个人博客:http://codingo.xyz