簡體   English   中英

如何像包一樣解碼C結構-使用Java套接字編程

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM