自定義數據格式的方案
一般傳送的數據,肯定有消息類型,消息序列號,具體的自定義消息內容;
則可以先定義一個統一的格式:
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 + "}";
}
}