有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何用javasocket编程解码C类结构数据包

java!中的socket编程新手!。我正在尽我最大的努力去理解javasocket编程,以及如何解码类似“C”的数据包

客户端是python,它在标记之间发送数据包, 示例有效负载:<tag1> data packet 1</tag1><tag2>data packet 2</tag2>

有一个c代码,用于接收有效载荷并对有效载荷进行结构解码,所有标签均为30字节,数据包1,2,。。n将具有不同的结构

我对java程序相当陌生,在通过web搜索之后,我可以编写一个socket服务器,从客户端接收有效负载,现在面临数据包解码问题。 我可以显示字节。所以使用数组。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。爪哇

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数据具有整数值

字符串值显示正确
在将字节转换为整数时发现问题,不确定数组中的STARTOC和offset值是否正确。copyOfRange方法

我根据从网上得到的信息尝试了这个方法

我还了解到,对于所有数据结构,都有一个不带方法的独立类。但我找不到任何完整的示例,因为java中没有“sizeof”

有人能告诉我在这种情况下解码数据包的正确方法吗


共 (2) 个答案

  1. # 1 楼答案

    对于字节数组或输入/输出,可以使用字节缓冲

    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

  2. # 2 楼答案

    如果您认为数据是一个要作为数据类型读取的流,那么可以扩展DataInputStream来创建一个PacketInputStream具有readDataPacket()方法。定义一个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类,但我对实际数据了解不够,无法解决这个问题