Netty之私有协议栈开发
一.私有协议介绍
私有协议本质上是厂商内部发展和采用的标准,除非授权,其他厂商一般无权使用该协议。私有协议也称非标准协议,就是未经国际或国家标准化组织采纳或批准,由某个企业自己制定,协议实现细节不愿公开,只在企业自己生产的设备之间使用的协议。
事实上,私有协议并没有标准定义,只有是能够用于跨进程,跨主机数据交换的非标准协议,都可以称为私有协议,通常情况下,正规的私有协议都有具体的协议规范文档,但是在在实际项目中,内部使用的私有协议往往是口头约定的规范,由于并不需要对外呈现或者被外部调用,所以一般不会单独写相关的内部私有协议规范文档。
二.私有协议栈开发
2.1 定义数据结构类NettyMessage实现
package aggrement;
importcom.sun.xml.internal.ws.api.message.Header;
public class NettyMessage {
privateHeader header; // 消息头
privateObject body; // 消息体
publicfinal Header getHeader(){
returnheader;
}
publicfinal void setHeader(Header header) {
this.header=header;
}
publicfinal Object getBody(){
returnbody;
}
publicfinal void setBody(Object body) {
this.body=body;
}
publicString toString(){
return"NettyMessage [header="+header+"]";
}
}
2.2 消息头Header实现
package aggrement;
import java.util.HashMap;
import java.util.Map;
public class Header {
privateint crcCode=oxabef0101;
privateint length; // 消息长度
privatelong sessionID; // 会话ID
privatebyte type; // 消息类型
privatebyte priority; // 消息优先级
privateMap<String,Object> attachment=new HashMap<String,Object>();
publicfinal int getCrcCode() {
returncrcCode;
}
publicfinal void setCrcCode(int crcCode) {
this.crcCode= crcCode;
}
publicfinal int getLength() {
returnlength;
}
publicfinal void setLength(int length) {
this.length= length;
}
publicfinal long getSessionID() {
returnsessionID;
}
publicfinal void setSessionID(long sessionID) {
this.sessionID= sessionID;
}
publicfinal byte getType() {
returntype;
}
publicfinal void setType(byte type) {
this.type= type;
}
publicfinal byte getPriority() {
returnpriority;
}
publicfinal void setPriority(byte priority) {
this.priority= priority;
}
publicfinal Map<String, Object> getAttachment() {
returnattachment;
}
publicfinal void setAttachment(Map<String, Object> attachment) {
this.attachment= attachment;
}
}
由于心跳消息,握手请求和握手应答消息都可以统一由NettyMessage承载,所以不需要为这几类控制消息做单独的数据结构定义。
2.3 消息编码类NettyMessageEncoder实现
package aggrement;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
importio.netty.channel.ChannelHandlerContext;
importio.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
/*
* 消息编码类
*/
public class NettyMessageEncoder extendsMessageToMessageEncoder<NettyMessage>{
MarshallingEncodermarshallingEncoder;
publicNettyMessageEncoder() throws IOException{
this.marshallingEncoder=newMarshallingEncoder();
}
protectedvoid encode(ChannelHandlerContext ctx,NettyMessage msg,List<Object> out)
throwsException {
if(msg==null||msg.getHeader()==null)
thrownew Exception("The encode message is null");
ByteBuf.sendBuf=Unpooled.buffer();
sendBuf.writeInt(msg.getHeader().getCrcCode());
sendBuf.writeInt(msg.getHeader().getLength());
sendBuf.writeLong(msg.getHeader().getSessionID());
sendBuf.writeByte(msg.getHeader().getType());
sendBuf.writeByte(msg.getHeader().getPriority());
sendBuf.writeInt(msg.getHeader().getAttachment().size());
Stringkey=null;
byte[]keyArray=null;
Objectvalue=null;
for(Map.Entry<String,Object>param:msg.getHeader().getAttachment().entrySet()){
key=param.getKey();
keyArray=key.getBytes("UTF-8");
sendBuf.writeInt(keyArray.lenght);
sendBuf.writeBytes(keyArray);
value=param.getValue();
marshallingEncoder.encode(value,sendBuf);
}
key=null;
keyArray=null;
value=null;
if(msg.getBody()!=null){
marshallingEncoder.encode(msg.getBody(),sendBuf);
} else{
sendBuf.writeInt(0);
sendBuf.senInt(4,sendBuf.readableBytes());
}
}
}
2.4 消息编码工具类MarshallingEncoder实现
package aggrement;
import java.io.IOException;
import org.jboss.marshalling.Marshaller;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
importio.netty.handler.codec.marshalling.MarshallingDecoder;
/*
* 消息编码工具类
*/
public class MarshallingEncoder {
privatestatic final byte[] LENGTH_PLACEHOLDER=new byte[4];
Marshallermarshaller;
publicMarshallingEncoder() throws IOException{
marshaller=MarshallingCodeFactory.buildMarshalling();
}
protectedvoid encode(Object msg,ByteBuf out) throws Exception{
try{
intlengthPos=out.writeIndex();
out.writeBytes(LENGTH_PLACEHOLDER);
ChannelBufferByteOutputoutput=new ChannelBufferByteOutput(out);
marshaller.start(output);
marshaller.writeObject(msg);
marshaller.finish();
out.setInt(lengthPos,out.writerIndex()-lengthPos-4);
}catch (Exception e) {
//TODO: handle exception
}finally {
marshaller.close();
}
publicclass NettyMessageDecoder extends LengthFieldBasedFrameDecoder {
MarshallingDecodermarshallingDecoder;
publicNettyMessageDecoder(int maxFrameLength,int lenthFieldOffset,
intlenthFieldLength) throws IOException {
super(maxFrameLength,lengthFieldOffset,lengthFieldLength);
marshallingDecoder=newMarshallingDecoder();
}
}
protectedObject decode(ChannelHandlerContext ctx,
ByteBufin) throws Exception {
ByteBufframe=(ByteBuf) super.decode(ctx,in);
if(frame==null){
returnnull;
}
NettyMessagemessage=new NettyMessage();
Headerheader=new Header();
header.setCrcCode(in.readerInt());
header.setLength(in.readerInt());
header.setSessionID(in.readLong());
header.setType(in.readByte());
header.setPriority(in.readByte());
intsize=in.readInt();
if(size>0){
Map<String,Object>attach=new HashMap<String,Object>(size);
intkeySize=0;
byte[]keyArray=null;
Stringkey=null;
for(inti=0;i<size;i++){
keySize=in.readInt();
keyArray=newbyte[keySize];
in.readBytes(keyArray);
key=newString(keyArray,"UTF-8");
attach.put(key,marshallingDecoder.decode(in));
}
keyArray=null;
key=null;
header.setAttachment(attach);
}
keyArray=null;
key=null;
header.setAttachment(attach);
}
if(in.readableBytes()>4){
message.setBody(marshallingDecoder.decode(in));
}
message.setHeader(header);
returnmessage;
}
}
2.5 消息解码类NettyMessageDecoder实现
package aggrement;
import io.netty.buffer.ByteBuf;
importio.netty.channel.ChannelHandlerContext;
importio.netty.handler.codec.LengthFieldBasedFrameDecoder;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/*
* 消息解码类
*LengthFieldBasedFrameDecoder支持自动的TCP粘包和半包处理,只需要给出标识消息长度的字段偏移量
* 和消息长度自身所占的字节数,netty就能自动实现对半包的处理
* 对于业务解码器来说,调用父类LengthFieldBasedFrameDecoder的解码方法后,返回的就是整包消息或者为空,
* 如果为空说明是个半包消息,直接返回继续由I/O线程读取后续的码流
*/
public class NettyMessageDecoder extends
LengthFieldBasedFrameDecoder{
MarshallingDecodermarshallingDecoder;
publicNettyMessageDecoder(int maxFrameLength,int lengthFieldOffset,
intlengthFieldLength) throws IOException{
super(maxFrameLength,lengthFieldOffset,lengthFieldLength);
marshallingDecoder=newMarshallingDecoder();
}
@Override
protectedObject decode(ChannelHandlerContext ctx,ByteBuf in) throws Exception {
ByteBufframe=(ByteBuf) super.decode(ctx,in);
if(frame==null){
returnnull;
}
NettyMessagemessage=new NettyMessage();
Headerheader=new Header();
header.setCrcCode(in.readInt));
header.setLength(in.readLong());
header.setSessionID(in.readLong());
header.setType(in.readByte());
header.setPriority(in.readByte());
intsize=in.readerInt();
if(size>0){
Map<String,Object>attch=new HashMap<String,Object>(size);
intkeySize=0;
byte[]keyArray=null;
Stringkey=null;
for(inti=0;i<size;i++){
keySize=in.readInt();
keyArray=newbyte[keySize];
in.readBytes(keyArray);
key=newString(keyArray,"UTF-8");
attch.put(key,marshallingDecoder.decode(in));
}
keyArray=null;
key=null;
header.setAttachment(attch);
}
if(in.readableBytes()>4){
message.setBody(marshallingDecoder.decode(in));
}
message.setHeader(header);
returnmessage;
}
}
2.6 消息解码工具类MarshallingDecoder实现
package aggrement;
import io.netty.buffer.ByteBuf;
import org.jboss.marshalling.ByteInput;
import org.jboss.marshalling.Unmarshaller;
/*
* 消息解码工具类
*/
public class MarshallingDecoder {
privatefinal Unmarshaller unmarshaller;
public MarshllingDecoder() throws IOException{
unmarshller=MarshallerCodeFactory.buildUnMarshalling();
}
protected Object decode(ByteBuf in) throws Exception{
int ObjectSize=in.readInt();
ByteBuf buf=in.slice(in.readerIndex(),objectSize);
ByteInput input=newChannelBufferByteInput(buf);
try {
unmarshaller.start(input);
Objectobj=unmarshaller.readObject();
unmarshaller.finish();
in.readerIndex(in.readerIndex()+objectSize);
returnobj;
}catch (Exception e) {
//TODO: handle exception
}finally {
unmarshaller.close();
}
}
}