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.