【问题标题】:how to decode C stuct like packets - with java socket programming如何像数据包一样解码 C 结构 - 使用 java 套接字编程
【发布时间】:2015-07-09 12:14:50
【问题描述】:

Java 中的套接字编程新手!。我正在尽我最大的努力去理解 Java 套接字编程,以及如何像数据包一样解码“C”支柱。

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

有一个接收payload的c代码,解码payload的结构体,所有tag都是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;

我收到带有以下代码 sn-p 的缓冲区

  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 和偏移值。

我根据从网上获得的信息进行了尝试。

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

有人可以指导我在这种情况下解码数据包的正确方法吗?

【问题讨论】:

    标签: java sockets


    【解决方案1】:

    对于字节数组或输入/输出,您可以使用 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 端的一个问题是字段对齐和平台可移植性。可以使用宏将结构转换为完全指定的二进制数据结构构建调用。


    在 cmets 之后,使用 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。

    【讨论】:

    • 感谢您的信息。认为这应该适用于任意数量的字节数组。
    • 我收到 181 个字节。 5 个数据为 32 个字节,具有字符串值,3 个 4 个字节的数据具有 int 值。所以 5*32 = 160 字节和 3*4=12 字节。在 181 个中,我能够成功解码 160 个字节的字符串。在剩下的 21 个字节中,我认为其他值,如 dataType、numBytes(请参阅我的主要问题 struct def GEN_VAL 和 GEN_STR_32)也打包在这里?!不知道如何解码这个整数值!
    • 许多格式都有你所拥有的,一个标记:dataType 来控制读取的内容。你可以选择DataInputStream,而不是 ByteBuffer。
    • 我正在使用 DataInputStream 从套接字接收数据,请参阅主要问题中给出的代码 sn-p。数据包在 RecvBuff 字节缓冲区中接收。而 ByteBuffer 仅用于获取整数值。还是我错过了什么?
    • 是否会阻塞套接字,如果我保留 DataInputStream 对象来处理数据?必须首先解码开始标签并循环获取数据,直到我根据特定标签到达结束标签。我认为在那个时间点接收套接字中可用的数据(在字节缓冲区中,因为二进制数据是通过套接字接收的)并在不同的线程中处理数据会很好。但是 DataInputStream 是获取数据的好方法!
    【解决方案2】:

    如果您将数据视为要作为数据类型读取的流,则可以扩展 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 类,但我对您的实际数据了解得不够多。

    【讨论】:

    • 哪个包包含 PacketInputStream ?认为它不是java的一部分?任何单独的图书馆?
    • PackageInputStream 是您可以通过扩展 java.io.DataInputStream 并添加您自己的 readPacket() 方法来创建的类。在该方法中,您可以调用超类的读取方法,例如read(byte[] b)readShort()
    • 注意Packet只是另一种要解码的数据类型,就像Stringshort一样,只是它由DataInputStream已经处理的更基本的数据类型组成。跨度>
    • 感谢凯文。发现困惑/难以以面向对象的方式可视化..必须尝试更多。
    • 我对示例代码进行了重新设计,以更接近您的 C 结构约定并更明确地了解 OO。
    猜你喜欢
    • 2012-11-20
    • 2013-11-28
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 2015-05-07
    • 1970-01-01
    • 2014-12-09
    相关资源
    最近更新 更多