[英]how to decode C stuct like packets - with java socket programming
Java中套接字編程的新功能! 我正在盡我最大的努力來理解Java套接字編程,以及如何像數據包一樣解碼“ C”支桿。
客戶端是python,它在標簽之間發送數據包,例如有效負載: <tag1> data packet 1</tag1><tag2>data packet 2</tag2>
具有接收有效載荷並用結構解碼有效載荷的ac代碼,所有標簽均為30字節,數據包1、2,.. n將具有不同的結構
我是java程序的新手,在網上搜索后,我可以編寫一個套接字服務器來接收來自客戶端的有效負載,現在在解碼數據包時遇到問題。 我可以顯示字節。 因此使用Arrays.copyOfRange方法獲取數據。
包數據如下所示:
typedef struct {
unsigned char dataType;
unsigned char value[4];
} GEN_VAL
typedef struct {
unsigned char dataType;
unsigned char numBytes;
unsigned char value[32];
} GEN_STR_32;
typedef struct {
char startTag[32];
GEN_STR_32 c_code;
GEN_STR_32 c_name;
GEN_STR_32 c_info;
GEN_VAL i_type;
GEN_VAL i_count1;
GEN_VAL i_count2;
char endTag[32];
} DATA_PACKET_1;
我收到以下代碼片段的緩沖區
DataInputStream inStream = new DataInputStream
(new BufferedInputStream(socket.getInputStream()));
while ( ( noOfBytes = (int)inStream.read(recvBuff)) != -1)
{
cDecode mydecode = new cDecode();
mydecode.decodePacket(noOfBytes, recvBuff);
}
cDecode.java
public void decodePacket(int TotalBytes, byte[] BuffRecv)
{
byte[] GetTag = new byte[32];
byte[] GEN_STR_32 = new byte[32];
byte[] GEN_INT_4 = new byte[4];
GetTag = Arrays.copyOfRange(BuffRecv,0,31);
String TagStr = new String(GetTag).trim(); //get start tag
int startloc = 31;
int offset = startloc + 32;
GEN_STR_32 = Arrays.copyOfRange(Buff,startloc,offset);
String cCode = new String(GEN_STR_32).trim(); // get value of cCode
System.out.println("Code :" + cCode );
startloc = offset;
offset = startloc + 32;
GEN_STR_32 = Arrays.copyOfRange(Buff,startloc,offset);
String cName = new String(GEN_STR_32).trim(); //get value of cName
System.out.println("Name:" + cName );
startloc = offset;
offset = startloc + 32;
GEN_STR_32 = Arrays.copyOfRange(Buff,startloc,offset);
String cInfo = new String(GEN_STR_32).trim(); //get value of cName
System.out.println("Info:" + cInfo );
startloc = offset;
offset = offset + 4;
ByteBuffer iTypeByteBuff = ByteBuffer.wrap(GEN_DATA_INT_4);
int iType= iTypeByteBuff .getInt();
System.out.println("type :" + iType);
startloc = offset;
offset = offset + 4;
// likewise used ByteBuffer for remaining integer data
// for receiving end tag, offset is added with 32!
}
前3個數據是字符串值,
后3個數據具有整數值
字符串值顯示正確。
在將字節轉換為整數時發現問題,不確定我是否在Arrays.copyOfRange方法中為startloc和offset使用了正確的值。
我根據從網上獲得的信息嘗試了此操作。
我還閱讀了關於有一個單獨的類,其中沒有用於所有數據結構的方法的信息。 但我找不到任何完整的示例,因為'sizeof'在Java中不可用。
有人可以指導我在這種情況下解碼數據包的正確方法。
對於字節數組或輸入/輸出,可以使用ByteBuffer。
byte[] bytes = ...
ByteBuffer buf = ByteBuffer.wrap(bytes);
buf.order(ByteOrder.LITTLE_ENDIAN); // Intel byte order.
short sh = buf.getShort(sh); // Java short = 2B
int unsignedSh = buf.getShort() & 0xFFFF; // Unsigned short emulation
ByteBuffer buf = ByteBuffer.allocate(4);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putShort(sh); // Java short = 2B
buf.putShort((short) unsignedSh);
還存在一個二進制格式的標准ASN ,可以處理更多的語法,但是在這種情況下,上面的方法可以做到。
C端的一個問題是字段對齊和平台可移植性。 可以使用宏將結構轉換為完全指定的二進制數據結構構建調用。
注釋后,使用DataInputStream
您似乎沒有對DataInputStream使用特定於數據的讀取調用。
enum DataType {
X0,
GEN_VAL,
X2,
X3,
GEN_STR_32,
...
}
class GenStr32 {
final DataType dataType = DataType.GEN_STR_32;
int numbytes; // 0..255
String value; // 32 bytes incl. '\0' in C
}
void readAnyTyped(DataInputStream in) {
int dataTypeIx = in.readByte() & 0xFF;
DataType dataType = DataType.values()[dataTypeIx];
switch (dataType) {
case GEN_STR_32:
GenStr32 data = new GenStr32();
data.numbytes = in.readByte();
byte[] bytes = new byte[data.numbytes]; // or 32?
bytes = in.readFully();
int length = 0;
while (length < bytes.length && bytes[length] != 0) {
++length;
}
data.value = new String(bytes, 0, length,
StandardCharsets.ISO_8859_1);
process(data);
break;
}
}
DataInputStream可能更直接。 ByteBuffer具有可指定字節順序的優勢,因為java默認為BIG_ENDIAN。
如果您將數據視為要作為數據類型讀取的流,則可以擴展DataInputStream
來創建具有readDataPacket()
方法的PacketInputStream
。 定義一個DataPacket
類來保存C DATA_PACKET_1
結構中的數據。
首先是保存數據的類:
public class GenValue {
private final byte dataType;
private final byte[] value;
public GenVal(byte dataType, byte[] value) {
this.dataType = dataType;
this.value = value;
}
public byte getDataType() {
return dataType;
}
public byte[] getValue() {
return value;
}
}
public class DataPacket {
private final String startTag;
private final String code;
private final String name;
private final String info;
private final GenValue type;
private final GenValue count1;
private final GenValue count2;
private final String endTag;
public DataPacket(String startTag, other args here...) {
this.startTag = startTag;
// Set the other properties from constructor args...
}
public String getStartTag() {
return startTag;
}
// Add getters for the other properties...
}
以及DataInputStream
實現來解碼您的數據包:
public class PacketInputStream extends DataInputStream {
public DataPacket readDataPacket() throws IOException {
String startTag = readGenStr32();
String code = readGenStr32();
// Do the same for name and info...
...
GenValue type = readGenValue();
GenValue count1 = readGenValue();
GenValue count2 = readGenValue();
// Read the endTag the same as the Strings above...
...
return new DataPacket(startTag, code, name, info, type, count1, count2, endTag);
}
public String readGenStr32() throws IOException {
byte[] strBuf = new byte[32];
readFully(strBuf, 0, 32);
return new String(strBuf).trim();
}
public GenValue readGenValue() throws IOException {
byte dataType = readByte();
byte[] value = new byte[4];
readFully(value);
return new GenValue(dataType, value);
}
}
調用代碼將執行以下操作:
PacketInputStream in = new PacketInputStream((new BufferedInputStream(socket.getInputStream()));
DataPacket p = in.readDataPacket();
System.out.println("Start tag: " + p.getStartTag());
System.out.println("Code: " + p.getCode());
// Print other values of interest...
我只是重寫DataPacket.toString()
,然后執行System.out.println(p)
,但是這里已經有足夠的代碼。 另外,我將存儲原始類型而不是使用GenValue
類,但是我對您的實際數據了解不足以解決這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.