简体   繁体   中英

how to decode C stuct like packets - with java socket programming

new to socket programming in java!. i am trying my level best to understand java socket programming, and how to decode 'C' strut like packets.

The client is python which sends packet between tags, example Payload : <tag1> data packet 1</tag1><tag2>data packet 2</tag2>

Have ac code to which received the payload and decode the payload with structure , all the tags are of 30 bytes, data packet 1, 2,.. n will be of different structure

I am fairly new to java program, after searching through the web, i could write a socket server which receives payload from client, now facing problem in decoding the data packets. I could display the bytes. so Used Arrays.copyOfRange method to get data.

The packet data are something represented as below,

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;

I receive buffer with following code snippet

  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!
}

first 3 data is string value and
second 3 data have integer value

String value is displayed correctly.
finding problem in converting byte to integer, not sure whether i am using correct value for startloc and offset in Arrays.copyOfRange method.

I tried this based on the information i got from net.

I also read about having a separate class without methods for all data structures. but i cant find any complete example, as 'sizeof' is not available in java.

can some one guide me the correct way to decode packets in this scenario.

For byte arrays or input/output you can use a 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);

There also exists a binary format standard ASN , to work more grammar like, but in this case the above will do.

One problem on the C side is field alignment and platform portability. One could use macros to transform a struct into a fully specified binary data structure building call.


After comments, using a DataInputStream

You did not seem to use the data specific read calls for 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 might be more direct. ByteBuffer has the advantage of the specifiable byte order, as java defaults to BIG_ENDIAN.

If you think of the data as a stream to be read as a data type, you could extend DataInputStream to create a PacketInputStream that has a readDataPacket() method. Define a DataPacket class to hold the data from your C DATA_PACKET_1 struct.

First the classes to hold your data:

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...
}

And the DataInputStream implementation to decode your packets:

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);
    }
}

Calling code would do something like:

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...

I would just override DataPacket.toString() and then do System.out.println(p) , but there's enough code here already. Also, I would store primitive types instead of using a GenValue class, but I don't know enough about your actual data to work that out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM