I have the following code. I can do the reading all well and convert data into hex. The issue is when I send back String replyMessage = "7E81";, the device receives it as "3745". What is wrong? Is it due to my encoding, or do I need to do some conversion before I send back?
w = new BufferedWriter(new OutputStreamWriter(receivedSocketConn1.getOutputStream(),"ISO-8859-15")); //
r = new BufferedReader(new InputStreamReader(receivedSocketConn1.getInputStream(),"ISO-8859-15"));
int nextChar=0;
while ((nextChar=r.read()) != -1) {
StringBuilder sb = new StringBuilder();
sb.append(Integer.toHexString(nextChar));
if (sb.length() < 2) {
sb.insert(0, '0'); // pad with leading zero if needed
}
String hexChar = sb.toString();
System.out.println("\n\n hex value is "+Integer.toHexString(nextChar).toUpperCase()+" "+"Int value is:"+nextChar);
message = message+hexChar;
String messageID=message.substring(2,6);
System.out.println("messageId is :"+messageID);
if(messageID.equals("0100")){
String replyMessage = "7E81";
w.write(replyMessage+"\r\n");
w.flush();
}
}
Based on comments in chat:
the documentation say
Start Byte (1 Byte) 7e
Message ID (2 Byte) 01 00
Message Body Nature (2 Byte) 00 19
Phone no. of Device (6 Byte) 09 40 27 84 94 70
Message Serial number (2 Byte) 00 01
Message Body (N Byte) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 40 27 84 94 70 00
Check Code (1Byte) 19
End Byte (1Byte) 7E
so the start and termination is 7E
for outgoing
Start Byte (1 Byte) 7e
Message ID (2 Byte) 81 00
Message Body Nature (2 Byte) 00 13
Phone no. of Device (6 Byte) 09 40 27 84 94 70
Message Serial number (2 Byte) 00 01
Message Body (N Byte) 00 01 00 32 30 31 31 31 31 30 38 31 31 33 33 32 31 39 36
Check Code (1Byte) 9A
End Byte (1Byte) 7e
This means the protocol in question is a binary protocol, not a textual protocol that sends hex strings, like you thought. As such, your use of OutputStreamWriter, InputStreamReader, StringBuilder, toHexString(), etc are all completely wrong for this protocol.
Each message received and sent begins with a fixed 13-byte header, followed by a variable-length body (the header specifies the body length), and terminated by a fixed 2-byte footer.
With that in mind, try something more like this instead:
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
...
w = new DataOutputStream(new BufferedOutputStream(receivedSocketConn1.getOutputStream()));
r = new DataInputStream(new BufferedInputStream(receivedSocketConn1.getInputStream()));
...
if (r.readByte() != 0x7E) // start byte
{
// ah oh, something went wrong!!
receivedSocketConn1.close();
return;
}
int messageID = r.readUnsignedShort(); // message ID
int bodyLen = r.readUnsignedShort(); // message body nature (body length)
byte[] phoneNum = new byte[6];
r.readFully(phoneNum); // device phone number
int serialNum = r.readUnsignedShort(); // message serial number
byte[] messageBody = new byte[bodyLen]; // message body
r.readFully(messageBody);
byte checkCode = r.readByte(); // check code
if (r.readByte() != 0x7E) // end byte
{
// ah oh, something went wrong!!
receivedSocketConn1.close();
return;
}
// TODO: validate checkCode if needed...
System.out.println("messageId is : 0x" + Integer.toHexString(messageID));
System.out.println("phoneNum is : " + bytesToHex(phoneNum));
System.out.println("serialNum is : 0x" + Integer.toHexString(serialNum));
System.out.println("messageBody is : " + bytesToHex(messageBody));
// process message data as needed...
switch (messageID)
{
case 0x100:
{
// ...
byte[] replyBody = new byte[19];
replyBody[0] = 0x00;
replyBody[1] = 0x01;
replyBody[2] = 0x00;
replyBody[3] = 0x32;
// and so on...
checkCode = 0x9A; // calculate as needed...
w.writeByte(0x7e); // start byte
w.writeShort(0x8100); // message ID
w.writeShort(replyBody.length); // message body nature (body length)
w.write(phoneNum); // device phone number
w.writeShort(0x0001); // message serial number
w.write(replyBody); // message body
w.writeByte(checkCode); // check code
w.writeByte(0x7e); // end byte
break;
}
// other message IDs as needed...
}
w.flush();
Related
I am trying to convert a Java function in C#. Here is the original code:
class SecureRandomString {
private static SecureRandom random = new SecureRandom();
private static Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
public static String generate(String seed) {
byte[] buffer;
if (seed == null) {
buffer = new byte[20];
random.nextBytes(buffer);
}
else {
buffer = seed.getBytes();
}
return encoder.encodeToString(buffer);
}
}
And here is what I did in C#:
public class Program
{
private static readonly Random random = new Random();
public static string Generate(string seed = null)
{
byte[] buffer;
if (seed == null)
{
buffer = new byte[20];
random.NextBytes(buffer);
}
else
{
buffer = Encoding.UTF8.GetBytes(seed);
}
return System.Web.HttpUtility.UrlPathEncode(RemovePadding(Convert.ToBase64String(buffer)));
}
private static string RemovePadding(string s) => s.TrimEnd('=');
}
I wrote some testcases:
Assert(Generate("a"), "YQ");
Assert(Generate("ab"), "YWI");
Assert(Generate("abc"), "YWJj");
Assert(Generate("abcd"), "YWJjZA");
Assert(Generate("abcd?"), "YWJjZD8");
Assert(Generate("test wewqe_%we()21-3012"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTI");
Assert(Generate("test wewqe_%we()21-3012_"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTJf");
Assert(Generate("test wewqe_%we()21-3012/"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTIv");
Assert(Generate("test wewqe_%we()21-3012!"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTIh");
Assert(Generate("test wewqe_%we()21-3012a?"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTJhPw");`
And everything works fine, until I try the following one:
Assert(Generate("test wewqe_%we()21-3012?"), "dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTI_");
My code output dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTI/ instead of the expected dGVzdCB3ZXdxZV8ld2UoKTIxLTMwMTI_. Why?
I think that the culprit is the encoder. The original code configure its encoder like this Base64.getUrlEncoder().withoutPadding(). The withoutPadding() is basically a TrimEnd("=") but I am not sure how to code the getUrlEncoder().
I looked into this handy conversion table URL Encoding using C# without finding nothing for my case.
I tried HttpUtility.UrlEncode but the output is not right.
What did I missed?
According to Oracle documentation, here is what getUrlEncoder() does:
Returns a Base64.Encoder that encodes using the URL and Filename safe type base64 encoding scheme.
Alright what is "URL and Filename safe". Once more the documenation is helping:
Uses the "URL and Filename safe Base64 Alphabet" as specified in Table 2 of RFC 4648 for encoding and decoding. The encoder does not add any line feed (line separator) character. The decoder rejects data that contains characters outside the base64 alphabet.
We can now look online for the RFC 4648. Here is the Table 2:
Table 2: The "URL and Filename safe" Base 64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 - (minus)
12 M 29 d 46 u 63 _
13 N 30 e 47 v (underline)
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y (pad) =
It is an encoding table. For example given 0 should output A, given 42 should ouput q, etc.
Let's check the decoding table, the Table 1:
Table 1: The Base 64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
Note that both table are strictly equals minus two things:
'+' is encoded to '-'
'/' is encoded to '_'
You should be able to fix your problem with:
private static string Encode(string s) => s.Replace("+", "-").Replace("/", "_");
To preface I am new to java (so take it ez on me lol) but here is what I am trying to do:
I have a GPS puck that I am reading via usb. The GPS puck uses some UI but I am trying to capture the data so I can use for my own application. I am reading in the packet from the serial buffer and trying to strip it down to just the payload and dissect the fields into a hashMap (not sure if this is the right application for it or not).
I want to add that the reason I care about efficiency in this case is because data will be getting processed through every fairly rapidly as the GPS puck is constantly getting Sat data (approx. every 200/300ms), so I am trying to avoid any bottlenecking if possible.
Here is the code that I have dished out, I know that in the code the buffer is not reading anything but in an effort to remove redundant code, just assume the data written in the comments is what we would be saying if we sys.out.
/*
Assume this is serial data received from GPS serial packet:
B5 62 01 02 1C AA AA AA AA 01 02 03 04 0A 0B 0C 0D B1 B2 B3 B4 C1 C2 C3 C4 D1 D2 D3 D4 E1 E2 E3 E4 01 02;
| | / | / |
| \ len \ / CRC
/ | \ /
/ | \ PAYLOAD /
Header | \ /
class & id \ /
*/
HashMap<String, String> payloadDict = new HashMap<String, String> (); // HashMap to write to
Bytes[] buffer; // serial buffer
String byteToHex = DataTypeConverter.parseHexBinary(buffer); // converts byte data to string in Hex
// Since the first few fields in the packet are static (header, class & id, and len),
// I strip the HEX string to only contain the payload
int payloadStartIndex = byteToHex.indexOf("C") + 1;
String payload = byteToHex.substring(payloadStartIndex, payloadStartIndex + 29);
/*
Assume this is the payload string after we parse:
AA AA AA AA 01 02 03 04 0A 0B 0C 0D B1 B2 B3 B4 C1 C2 C3 C4 D1 D2 D3 D4 E1 E2 E3 E4
**added seperation every 2 hex values for readability,
but assume it's just one giant string
*/
// write to HashMap
payloadDict = NAV_POSLLH(payload);
/*
so to visualize what I would imagine being saved to the HashMap:
iTOW: AA AA AA AA
lon: 01 02 03 04
lat: 0A 0B 0C 0D
height: B1 B2 B3 B4
hMSL: C1 C2 C3 C4
hAcc: D1 D2 D3 D4
vAcc: E1 E2 E3 E4
*/
public Map<String, String> NAV_POSLLH(String dataArr) {
// Temp HashMap
HashMap<String, String> dict = new HashMap<String, String> ();
/*
this is the payload format.
payloadStructure are the names of the all the fields I want to parse
and hexOffset are the beginning of each field.
*/
String[] payloadStructure = new String[6] {"iTOW", "lon", "lat", "height", "hMSL", "hAcc", "vAcc"};
int[] hexOffset = new int[6] {0, 7, 11, 15, 20, 24};
// loop through each field
for(int i = 0; i < byteOffset.length; i++) {
String parse = "";
// I am not sure if this is the best way to handle out of index errors
// in java, but this is what I thought would work
if(byteOffset[i] == 24) {
parse = dataArr.substring(hexOffset[i]);
map.put(payloadStructure[i], parse);
return dict;
}
// parse the substring from the beginning of each hex offset
// to the next index -1
parse = dataArr.substring(byteOffset[i], hexOffset[i+1] -1);
map.put(payloadStructure[i], parse);
// finally return HashMap
return dict;
}
}
I am not sure if the comments explain the packet structure well enough or if it is confusing (because the format is janky), so I attached a screenshot of the packet layout I made.
So what I am trying to ask from all of this:
Based on what I am trying to accomplish, is this an efficient or even a correct way of implementing it? I am not sure if there is anything I am just obviously missing.
I am unable to compile or test any code right now so I am just trying to visualize what the output would be haha. Thanks so much for anyone's input!
I would make a Packet class and fill it using ByteBuffer. That class has great methods for parsing, including bulk get methods for various primitive sizes. You might consider using a ByteBuffer for the payload member of that class
This question already has answers here:
How to convert byte array to hex format in Java
(4 answers)
Closed 2 years ago.
So i have a piece of code, which converted byte[] from a binary file into int[] which then went on to be converted to String.
Example: 02 09 1A would translate to "2926" using String.valueOf(byte);
But this is where the fun begins: the old Code already split up the array before it became String so it didn't matter. Now i have code which needs to somehow figure out if there is a 2 9 or 29... How can i get the String to
- stay in Hex instead of "switching" to decimal and
- keep the zeros in the string
so i can always grab the next two chars, figure out which info they display and go on?
Input: 05 06 1D 11 07 08 01 32 21 28 2F 20 2E 21 34 22 25 33 01 02 09 0A FF 0B 0C 2!(/ .!4"%3
ΓΏ
Current Output: 562917781503340473246335234375112910-11112
Expected Output: 05061D110708013221282F202E21342225330102090AFF0B0C
(Didn't put my old code in here since it's based on int which is crap)
If I understand correctly, you have a byte[] as input, and you want to output a string that is all those bytes, in hex form, joined together:
private static String byteArrayToString(byte[] byteArray) {
StringBuilder builder = new StringBuilder();
for (byte b : byteArray) {
int i = Byte.toUnsignedInt(b);
if (i < 16) {
builder.append("0");
}
builder.append(Integer.toHexString(i).toUpperCase());
}
return builder.toString();
}
Note the use of Byte.toUnsignedInt. This converts things like -1 the byte to the int 0xff. This is required because Java's bytes are signed.
Also note where I pad with 0. I only do this if the byte is a single digit in hex. i < 16 is the condition to check this.
Another way:
char[] hexVals = {'0', '1','2','3','4','5','6','7','8','9','A', 'B','C','D','E','F'};
byte[] bytes = {5, 6, (byte)0xAB, (byte)0xde}; // sample input
char[] output = new char[bytes.length*2];
// split each byte's hex digits into two chars
for(int i=0; i < bytes.length; i++) {
output[2*i] = hexVals[(bytes[i] >> 4) & 0xf]; // HO nibble
output[2*i+1] = hexVals[bytes[i] & 0xf]; // LO nibble
}
System.out.println(new String(output)); // 0506ABDE
I'm trying to convert a hex message to ascii
and I don't understand why I get this error
String final = (msg1 + " " + msg2 + " " + msg3);
byte[] s = DatatypeConverter.parseHexBinary(final);
String final = new String(s);
and this is the error I get :(the data is correct )
java.lang.IllegalArgumentException: hexBinary needs to be even-length: 4C 4B 4C 41 36 4C 31 45 32 48 41 37 32 32 31 36 31 FF
parseHexBinary is documented to take the input as defined by xsd:hexBinary.
This means the input string must contain only hexadecimal characters and always pairs. Your input stream contains space characters, that must be removed prior to parsing.
input = input.replaceAll(" +", "");
I'm sending a prepared statement to be executed by MySQL and it looks like I'm following the protocol correctly but the server always returns:
Error 1210 - #HY000 - Incorrect arguments to mysqld_stmt_execute
The message bytes are encoded as follows:
14 00 00 - size
00 - sequence
17 - message type
01 00 00 00 - statement id
00 - flags
01 00 00 00 - iteration count
80 - NULL bitmap (1000-0000)
01 - new params bound flag
06 00 - NULL type
03 00 - INT type
0a 00 00 00 - 10 INT value
So I can't really understand what's different on this message to what MySQL expects and it doesn't look like I can get MySQL to explain this to me.
The SQL statments sent to the server are:
CREATE TEMPORARY TABLE timestamps ( id INT NOT NULL, moment TIMESTAMP, primary key (id));
INSERT INTO timestamps (moment, id) VALUES (?, ?);
Any help is appreciated on this.
Your null-bitmap is wrong. Should be 01 and not 80.
From the doc:
NULL-bitmap-bytes = (num-fields + 7 + offset) / 8
NULL-bitmap-byte = ((field-pos + offset) / 8)
NULL-bitmap-bit = ((field-pos + offset) % 8)
Also from the doc, For COM_STMT_EXECUTE this offset is 0.
You have two fields: moment and id.
moment is null.
moment is field index 0.
NULL-bitmap-bytes = (num-fields + 7 + offset) / 8
-> (2 + 7 + 0) / 8
== 1
NULL-bitmap-byte = ((field-pos + offset) / 8)
-> ((0 + 0) / 8)
== 0
NULL-bitmap-bit = ((field-pos + offset) % 8)
-> ((0 + 0) % 8)
== 0
Thus, the Null bitmask is 1 byte, and to set the bit for field 0 we do:
nulls[byte_pos] |= 1 << bit_pos
which in your case is
nulls[0] |= 1 << 0;
thus nulls[0] is 01