自定义数据格式的方案
一般传送的数据,肯定有消息类型,消息序列号,具体的自定义消息内容;
则可以先定义一个统一的格式:
prefix + packType(1) + seq(4) +[userData](标志性前缀+消息类型+序列号+自定义数据)
封装成byte[] 和解析数据过程
针对上面根据每种消息类型自定义的userData,可以这样定义格式:
[filedType + filedLength+ filedValue](字段类型标志+字段长度+字段值)
直接看例子
packData ()为打包消息
public class Utils {
byte PACKET_PREFIX = '$';
int FILED_HEADER_LENGTH = 5;//因为filedType为byte + filedLength为int,所以字段前缀长度为5
int MAX_PACKET_LENGTH = 1024;//包大小 1k
int INT_LENGTH = 4;
/**
* 封装报文
* 协议:prefix + packType(1) + seq(4) +[userData]
* prefix 标志性前缀
* packType - 报文类型
* seq - 发送序列
* userData - 用户数据
*
* @param seq
* @param packType
* @param userData
* @return
*/
public static byte[] packData(int seq, byte packType, byte[] userData) {
if (userData == null) {
return null;
}
byte[] data = new byte[MAX_PACKET_LENGTH];
int offset = 0;
// 打包数据头部
//add prefix
data[offset++] = PACKET_PREFIX;
//add msgType
data[offset++] = packType;
//add seq
addInt(data, offset, seq);
offset += INT_LENGTH;
if (data.length < offset + userData.length) {//数组大小不够,则需要扩容
byte[] tmp = new byte[offset + userData.length];
System.arraycopy(data, 0, tmp, 0, offset);
data = tmp;
}
System.arraycopy(userData, 0, data, offset, userData.length);
offset += userData.length;
byte[] result = new byte[offset];
System.arraycopy(data, 0, result, 0, offset);
return result;
}
public static int bytesToInt(byte[] src, int offset) {
if (src == null || src.length < offset || offset + SearchConst.INT_LENGTH > src.length) {
return -1;
}
int sendSeq;
sendSeq = src[offset++] & 0xFF;
sendSeq |= (src[offset++] << 8) & 0xFF00;
sendSeq |= (src[offset++] << 16) & 0xFF0000;
sendSeq |= (src[offset++] << 24) & 0xFF000000;
return sendSeq;
}
public static byte[] intToBytes(int value) {
byte[] src = new byte[4];
src[0] = (byte) (value & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[3] = (byte) ((value >> 24) & 0xFF);
return src;
}
public static void addInt(byte[] src, int offset, int value) {
byte[] seqBytes = intToBytes(value);
System.arraycopy(seqBytes, 0, src, offset, 4);
}
/**
* [filedType + filedLength+ filedValue]
*
* @param src
* @param filedBytes
*/
public static void addFiledBytes(byte[] src, int offset, byte[] filedBytes, byte fieldType) {
src[offset++] = fieldType;
Utils.addInt(src, offset, filedBytes.length);
offset += INT_LENGTH;
System.arraycopy(filedBytes, 0, src, offset, filedBytes.length);
}
下面为自定义的数据DeviceData 如何和byte[] 相互转化
public class DeviceData extends BaseUserData {
private static final byte FIELD_TYPE_DEVID = 0x31;
private static final byte FIELD_TYPE_SERVICENAME = 0x32;
private static final byte FIELD_TYPE_PKGNAME = 0x33;
private static final byte FIELD_TYPE_FUNCTION = 0x34;
private String devId;//设备id
private String serviceName;
private String pkgName;
private int func;//功能位
public String getDevId() {
return devId;
}
public void setDevId(String devId) {
this.devId = devId;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getPkgName() {
return pkgName;
}
public void setPkgName(String pkgName) {
this.pkgName = pkgName;
}
public int getFunc() {
return func;
}
public void setFunc(int func) {
this.func = func;
}
/**
* [filedType + filedLength+ filedValue]
*
* @param device
* @return
*/
public static byte[] packDeviceData(DeviceData device) {
if (device == null || device.getDevId() == null || device
.getServiceName() == null || device.getPkgName() == null) {
return null;
}
try {
byte[] devIdBytes = device.getDevId().getBytes("UTF-8");
byte[] serviceNameBytes = device.getServiceName().getBytes("UTF-8");
byte[] pkgNameBytes = device.getPkgName().getBytes("UTF-8");
byte[] funcBytes = Utils.intToBytes(device.getFunc());
byte[] data = new byte[SearchConst.MAX_PACKET_LENGTH];
int offset = 0;
//add devId
Utils.addFiledBytes(data, offset, devIdBytes, FIELD_TYPE_DEVID);
offset += FILED_HEADER_LENGTH + devIdBytes.length;
//add serviceName
Utils.addFiledBytes(data, offset, serviceNameBytes, FIELD_TYPE_SERVICENAME);
offset += FILED_HEADER_LENGTH + serviceNameBytes.length;
//add pkgName
Utils.addFiledBytes(data, offset, pkgNameBytes, FIELD_TYPE_PKGNAME);
offset += FILED_HEADER_LENGTH + pkgNameBytes.length;
//add func
Utils.addFiledBytes(data, offset, funcBytes, FIELD_TYPE_FUNCTION);
byte[] result = new byte[data.length];
System.arraycopy(data, 0, result, 0, data.length);
return result;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public static DeviceData parseDeviceUserData(byte[] userData) {
DeviceData device = new DeviceData();
if (userData.length < FILED_HEADER_LENGTH) {
return null;
}
int offset = 0;
while (offset + FILED_HEADER_LENGTH < userData.length) {
byte dataType = userData[offset++];
int len = Utils.bytesToInt(userData, offset);
offset += INT_LENGTH;
if (len < 0 || len + offset > userData.length) {
return null;
}
switch (dataType) {
case FIELD_TYPE_DEVID:
String devId = null;
try {
devId = new String(userData, offset, len, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
device.setDevId(devId);
break;
case FIELD_TYPE_SERVICENAME:
String serviceName = null;
try {
serviceName = new String(userData, offset, len, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
device.setServiceName(serviceName);
break;
case FIELD_TYPE_PKGNAME:
String pkgName = null;
try {
pkgName = new String(userData, offset, len, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
device.setPkgName(pkgName);
break;
case FIELD_TYPE_FUNCTION:
int func = Utils.bytesToInt(userData, offset);
if (func > 0) {
device.setFunc(func);
}
break;
default:
}
offset += len;
}
return device;
}
@Override
public String toString() {
return "DeviceData={ip=" + ip + ",port=" + port + ",devId=" + devId + ",serviceName=" +
serviceName + ",pkgName=" + pkgName + ",func=" + func + "}";
}
}