本文共 5360 字,大约阅读时间需要 17 分钟。
闲言:关于mesos的学习,因为某些原因(我的性能好的台式暂时不可用了)而搁置,搁置时间不定
最近在对接某个第三方的socket接口的时候,对与这种写socket服务完全不考虑粘包拆包的操作完全绝望了,所以写一下粗浅的关于如何自定义协议,以及编写可处理粘包拆包的编解码器。关于tcp粘包拆包的概念请查阅其它博客
个人认为想能解决拆包和粘包问题,根本的途径就是知道消息有多长。大致常用的方法就是消息有指定的分隔符,或者在前面固定大小的消息头里存储剩余消息主体的大小
示例如下,实例基于netty编写,大致想法相同,不过netty免去了自己管理缓存
package protocol;import java.io.UnsupportedEncodingException;/** * Created by tangjiaqi on 2018/5/17. */public class Message { public enum MessageType{ BASE(1), DISCONNECT(2); int value; MessageType(int i) { value = i; } public int getValue(){ return value; } public static MessageType valueOf(int i){ MessageType[] types = MessageType.values(); MessageType result = null; for (MessageType x: types){ if (x.getValue() == i){ result = x; break; } } return result; } }// 协议头,标记协议从什么时候开始 private final static int HEADER = 0x7788b;// 消息类型 private MessageType messageType;// 字符集的字符串长度(考虑不同编码的数据) private int charsetLength;// 内容长度 private int contentLength;// 内容 private byte[] charset;// 字符集 private byte[] content; public Message() { } public Message(MessageType messageType, String content, String charset) throws UnsupportedEncodingException { this.messageType = messageType; this.content = content.getBytes(charset); this.charset = charset.getBytes(); this.contentLength = this.content.length; this.charsetLength = this.charset.length; } public MessageType getMessageType() { return messageType; } public void setMessageType(MessageType messageType) { this.messageType = messageType; } public int getCharsetLength() { return charsetLength; } public int getContentLength() { return contentLength; } public byte[] getContent() { return content; } public void setContent(String content, String charset) throws UnsupportedEncodingException { this.content = content.getBytes(charset); this.charset = charset.getBytes(); this.contentLength = this.content.length; this.charsetLength = this.charset.length; } public byte[] getCharset() { return charset; } public static int getHEADER() { return HEADER; }}
Encoder:
package codce;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;import protocol.Message;/** * Created by tangjiaqi on 2018/5/17. */public class MyProtocolEncoder extends MessageToByteEncoder{ public MyProtocolEncoder(){ } protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { out.writeInt(msg.getHEADER()); out.writeInt(msg.getMessageType().getValue()); out.writeInt(msg.getCharsetLength()); out.writeInt(msg.getContentLength()); out.writeBytes(msg.getCharset()); out.writeBytes(msg.getContent()); }}
encoder较为简单,按顺序写入ByteBuf就好了
Decoder:
package codce;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.ByteToMessageDecoder;import protocol.Message;import protocol.Message.MessageType;import java.util.List;/** * Created by tangjiaqi on 2018/5/17. */public class MyProtocolDecoder extends ByteToMessageDecoder { public MyProtocolDecoder(){ }// 协议头+消息类型+charsetLength+contentLength的长度 private final static int BASE_LENGTH = 16; protected void decode(ChannelHandlerContext ctx, ByteBuf in, List
关于解码器部分其实就几个注意点
达成以上两点大致就能做到解决拆包粘包问题了,关于这个协议还做了一个测试,测试大致就是一次性传输内容较大,然后查看接受到的内容以及解码过程
关于对这个自定义协议的demo应用详见:
ps: 这个自定义协议没有考虑任何传输内容的减少的优化以及其它七七八八的各种细节,很简单的那种
转载地址:http://aigmi.baihongyu.com/