I tried to implement native messaging protocol in java but it didn't work.
I tried it following way.
private String readMessage() {
int length = getInt(getLength());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
byte[] b = new byte[4];
try {
int total;
for(int totalRead = 0 ; totalRead < length ; totalRead = totalRead + 4){
System.in.read(b); // make sure
bOut.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
String bRes = null;
try {
bRes = new String(bOut.toByteArray(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return bRes;
}
To read size i have used following methods:
This construct the int from first four bytes
private int getInt(byte[] bytes)
{
return (bytes[3] << 24) & 0xff000000 |
(bytes[2] << 16) & 0x00ff0000 |
(bytes[1] << 8) & 0x0000ff00 |
(bytes[0] << 0) & 0x000000ff;
}
This reads first four bytes and returns byte array
private byte[] getLength()
{
int length = 0 ;
byte[] bytes = new byte[4];
try
{
System.in.read(bytes);
} catch (IOException e)
{
e.printStackTrace();
}
return bytes;
}
This gives "Error when communicating with the native messaging host" error. How can i implement this protocol correctly in java.
Can someone provide simple working example for java
My approach below gives a Java implementation that receives a message from a Chrome App and sends a message back. On my little-endian machine it works.
I haven't properly studied your effort but hopefully this will help with your 'simple working example' request.
The main points:
Communication is with the standard stream. As you know, read in the first 4 bytes separately to learn the length (here, in to lengthByte):
byte[] lengthByte = new byte[4];
int bytesRead = System.in.read(lengthByte,0,4);
//Read the message into byte[] c:
byte[] c = new byte[text_length];
int lengthAppMessage = System.in.read(c,0,text_length);
When writing back to the app, we write the message length in the 1st 4 bytes. For the message {"m":"hi"}, which is the message I send below, the message length is 10. (For {"m":"hello"} it's 13, etc.)
int returnedMessageLength = 10;
System.out.write((byte) (returnedMessageLength));
System.out.write((byte)0);
System.out.write((byte)0);
System.out.write((byte)0);
Where the last three lines are padding to sum to 4 bytes. You may need to put these three lines in to the stream before the message length.
When appending the message, the {"...":"..."} format is needed. We can send the message through in sections e.g.
System.out.append('{');
System.out.append('"');
System.out.append('m');
System.out.append('"');
System.out.append(':');
System.out.append('"');
System.out.append('h');
System.out.append('i');
System.out.append('"');
System.out.append('}');
The point is that breaking the message in to sections and sending each section separately circumnavigates the Java formatting problem (caused by the single outer quotes.)
Put all of the above code inside a never-ending 'while' loop to avoid exiting too soon. (To see this code running, I integrated it with the example from Google's native messaging page.)
This is not good code that I've used, but either by accident or design, it worked this way for me.
The below codes works well on my side.
//Convert length from Bytes to int
public static int getInt(byte[] bytes) {
return (bytes[3] << 24) & 0xff000000|
(bytes[2] << 16)& 0x00ff0000|
(bytes[1] << 8) & 0x0000ff00|
(bytes[0] << 0) & 0x000000ff;
}
// Read an input from Chrome Extension
static public String receiveMessage(){
byte[] b = new byte[4];
try{
System.in.read(b);
int size = getInt(b);
byte[] msg = new byte[size];
System.in.read(msg);
// make sure to get message as UTF-8 format
String msgStr = new String(msg, "UTF-8");
return msgStr;
}catch (IOException e){
e.printStackTrace();
return null;
}
}
You could use https://github.com/Cosium/web-native-messaging-host .
It is a java library allowing to turn any JVM application into a Web Native Messaging Host .
Related
I am reading data from serialport using jSerialComm package. I am getting following data
A total of 22 bytes are being received and I am getting these three garbage bytes too. The readable data is correct but what these garbage characters are happening?
Following is my code.
public static void main(String[] args) {
SerialPort serialPort = SerialPort.getCommPort("/dev/ttyUSB0");
if(serialPort.openPort())
{
System.out.println("Port Opened Successfully...");
}
else
{
System.out.println("Unable to open port....");
return;
}
serialPort.setComPortParameters(1200, 8, 1, 0);
try
{
while(true)
{
while(serialPort.bytesAvailable() != 0)
{
Thread.sleep(1000);
byte[] readBuffer = new byte[serialPort.bytesAvailable()];
int numRead = serialPort.readBytes(readBuffer, readBuffer.length);
String data = new String(readBuffer);
System.out.println("Read "+numRead+ " bytes." + readBuffer);
System.out.println(data);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
serialPort.closePort();
System.out.println("done...");
}
SerialPort.readBytes seems to be not thread safe, thus yielding "garbage" when called during character reception.
I suggest to use the snippet from the author's example which worked fine for me:
// Get a new instance of SerialPort by opening a port.
SerialPort port = SerialPort.open("COM2");
// Configure the connection
port.setTimeout(100);
port.setConfig(BaudRate.B115200, Parity.NONE, StopBits.ONE, DataBits.B8);
// You have the choice, you can either use the Java NIO channels
// or classic Input/Ouput streams to read and write data.
//DEL SerialChannel channel = port.getChannel();
InputStream istream = port.getInputStream();
// Read some data using a stream
byte[] byteBuffer = new byte[4096];
// Will timeout after 100ms, returning 0 if no bytes were available.
int n = istream.read(byteBuffer);
// *** Use n bytes of byteBuffer ***
//DEL ...
port.close();
//DEL : removed from the original code for clarity
You did not explain the protocol but I suggest to look it up. Presumably these are control characters or like a comment suggests binary data. You create a String from a byte buffer without an encoding so this also depends on your environment/ JVM's default encoding.
Try treating the first and the last two bytes as specified in the protocol in use for your project. It might also be related to jSerialComm not removing serial signalling, e.g. handshake, EOT and such.
If you're reverse-engineering the protocol instead maybe also try another library like RxTx to see if the bytes stay the same.
To inspect the bytes safely use for example BigInteger to print out a Hex-String instead:
BigInteger bigInteger = new BigInteger(1, bytes);
System.out.printf("%0" + (bytes.length << 1) + "x", bigInteger);
Use this code block and it will run perfectly;
serialPort.addDataListener(new SerialPortDataListener() {
#Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
}
#Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if (serialPortEvent.getEventType() != SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
return;
}
byte[] newData = serialPortEvent.getReceivedData();
String data = new String(newData);
System.out.println(data);
}
});
in my Client-Server application I found a strange error. I got the following Methods :
sendLoginResponse();
sendPlayerList();
sendCurrentLevelState();
Each Methods sends a byte array to the Client-Side
If I only call 2 of them, everything works fine and the Client-Side gets all the sent byte arrays. But if I call all 3 of them only the first and second one arrive the Client, the order of the following methods doesnt matter. but the Server says all of them were send. To write to the Client iam using the
write(byte[] b, int off, int len); method
all The lenghts within the packages make sense too.
Here comes the strange point:
if I add a Thread.sleep(1000); after the second Method, the third one does now arrive the Client after the sleep. I Have also tried to flush the DataOutputStream after every write call, but this didnt help.
EDIT:
So let's say I'd send 3 Login-Response
The Method's that gives me the byte[]:
public byte[] getLoginResponse(int playerID){
byte[] msg = new byte[4];
short shortMsgLength = 4;
byte[] msgLength = shortToBytes(shortMsgLength);
msg[0] = 2;
msg[1] = msgLength[0];
msg[2] = msgLength[1];
msg[3] = (byte) playerID;
return msg;
}
private byte[] shortToBytes(short value) {
byte[] returnByteArray = new byte[2];
returnByteArray[0] = (byte) (value & 0xff);
returnByteArray[1] = (byte) ((value >>> 8) & 0xff);
return returnByteArray;
}
And the Send Method:
private void sendLoginResponse() {
try{
byte[] msg = rfcObject.getLoginResponse(playerID);
out.write(msg,0,msg.length);
}catch(Exception e){
System.err.println(e.getMessage());
System.exit(0);
}
}
So if I call the sendLoginResponse(); three times in a row, the client only recieves 2 byte-arrays, but the server says it has been sent 3 times. If i add a
Thread.sleep(1000); `after the second Method-Call, everything works fine..`
The Client that reads the message runs in a Thread:
public void run(){
while(true){
try {
byte[] data = new byte[MAX_DATA_SIZE]; // MAX_DATA = 255
byteCount = in.read(data);
} catch (IOException ex) {
handleExceptionError(ex);
}
}
}
thank you!
if I call the sendLoginResponse(); three times in a row, the client only recieves 2 byte-arrays, but the server says it has been sent 3 times.
This is because TCP is a stream-oriented protocol. Meaning it doesn't know or care how your messages are delimited. There's no concept of individual messages in TCP, just a stream of bytes, with the guarantee that the order of bytes is preserved.
So when the sender calls three write, the three byte arrays are simply concatenated over the connection and arrives at the receiver in the same order, but the receiver doesn't necessarily need three read to get all the bytes, and even if it does take three read, the read doesn't necessarily gives you the same byte array passed to each corresponding write.
Your message already have the necessary information to get the individual message back from the byte stream:
// Client code for reading individual messages from a TCP connection
byte type = din.readByte();
// Read the message length, little-endian.
// We cannot use din.readShort because it's big-endian
int lenLo = din.read();
int lenHi = din.read();
short len = (short)(lenLo | (lenHi << 8));
byte [] body = new byte[len];
din.readFully(body);
DataOutputStream and TCP don't lose data.
As almost invariable seen in questions of this nature, the problem is at the receiving end. You are probably assuming that `read()' fills the buffer, and ignoring the count that it returns.
Based on your protocol description in comments, you should be using DataInputStream.readFully() in this circumstance:
byte type = din,readByte();
int length = din.readShort();
byte[] data = new byte[length];
din.readFully(data);
I have a problem with sending directory names over socket from my C++ client, to my Java server.
Sending ordinary messages like "hello world", works great , but the following doesn't and I can not figure out what the problem is:
char const * files = ffd.cFileName; // get directory name
string str(files, 0, strlen(files)); // convert pointer to string, right?
char mess[str.size()];
strcpy(mess, str.c_str()); // make char array :)
cout << "Send file: " << mess << " with strlen: " << strlen(mess) << " and sizeof: " << sizeof(mess) << endl;
int sent = 0;
if ((sent = send(connectSocket, mess, sizeof(mess), 0)) == SOCKET_ERROR)
{
closesocket(connectSocket);
WSACleanup();
connectToServer();
}
The java server just receives the directory names like this:
wam
p
Win
dow
s
Win
dow
s.o
ld
wxW
idg
ets
I can not understand what I'm missing because I have tried every possible way to do this and the C++ client prints like:
"Send file: windows with strlen: 7 and sizeof: 7"
I do not think that the java server is the problem since I can receive normal strings and messages perfectly, but anyway here is the JAVA code:
is = socket.getInputStream();
byteArray = new byteArray[1024];
while (true) {
c = is.read(byteArray, 0, byteArray.length);
String recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
If you request something else or anything just leave a comment and I will fix it.
Question: are you sending via TCP or UDP? I'm guessing TCP, and if that is the case, you need to treat the socket as more of a stream. That stream may get broken up into a bunch of packets - you don't really control that. What I might do is to prefix the string length of each directory (ex, 3foo, 4barz, etc), read from the socket and determine what constitutes as a logical block or string, and then assemble / print the strings based on that. If you go with that route, you need to track how much you read each time until you think you are done.
I solved it, Just added byteArray = new byte[1024]; and now it works:
while (true) {
byteArray = new byte[1024]; // I ADDED THIS AND NOW THE JAVA SERVER RECEIVES IT CORRECTLY!
c = is.read(byteArray, 0, byteArray.length);
recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
Hi I am getting the Data from the GPS device, I want to send the response back to the Gps device by creating the packet MY packet is like this.
No. Field TYPE Length description
1 sMark Char 6 Flag of message (\r\n*KW\0)
2 packetLe short 2 Message Length
3 CMD Short 2 0x8200
4 cErrorCodeChar 1 0x00OK, 0x01invalid deviceID
5 sEnd Char 2 message end "\r\n"
I want Crete Packet like this by using this code.
InetAddress IPAddress = receivePacket.getAddress();
String sendString = "polo";
sendData = sendString.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
serverSocket.send(sendPacket);
I am trying like this.
ByteArrayOutputStream bytearrypacket = new ByteArrayOutputStream();
DataOutputStream dateoutputpacket = new DataOutputStream(bytearrypacket);
dateoutputpacket.writeChars("\r\n*KW\0");
dateoutputpacket.writeShort(15);
dateoutputpacket.writeShort(82);
dateoutputpacket.writeChar('1');
dateoutputpacket.writeChars("\r\n");
dateoutputpacket.flush();
byte[] result = bytearrypacket.toByteArray();
Updated:
ByteArrayOutputStream bytearrypacket = new ByteArrayOutputStream();
DataOutputStream dateoutputpacket = new DataOutputStream(bytearrypacket);
dateoutputpacket.writeBytes("\r\n*KW\0");
dateoutputpacket.writeShort(15);
dateoutputpacket.writeShort(82);
dateoutputpacket.writeByte(1);
dateoutputpacket.writeBytes("\r\n");
dateoutputpacket.flush();
byte[] result = bytearrypacket.toByteArray();
Updated 2:
try {
ByteArrayOutputStream bytearrypacket = new ByteArrayOutputStream();
DataOutputStream dateoutputpacket = new DataOutputStream(bytearrypacket);
dateoutputpacket.writeBytes("\r\n*KW\0");
dateoutputpacket.writeShort(15);
dateoutputpacket.writeShort(82);
dateoutputpacket.writeByte(1);
dateoutputpacket.writeBytes("\r\n");
dateoutputpacket.flush();
byte[] result = bytearrypacket.toByteArray();
DatagramPacket responsepacket = new DatagramPacket(result, result.length, packet.getAddress(), packet.getPort());
dsocket.send(responsepacket);
} catch (IOException ex) {
ex.printStackTrace();
} catch (SecurityException se) {
se.printStackTrace();
} catch (IllegalBlockingModeException il) {
il.printStackTrace();
} catch (IllegalArgumentException ilg) {
ilg.printStackTrace();
}
Create a DataOutputStream around a ByteArrayOutputStream; use its various methods to write the data; then get the bytes from the ByteArrayOutputStream and wrap a DatagramPacket around that.
Ive quickly put this together. Using this function you can create a byte array which has all packet information in the correct place. I used some of the sample info from the question. That should be enough to send the info across the connection. you can pass the byte array directly into your datagram
public class Sample {
public static void main(String[] args){
byte[] packet = createMsg("\r\n*KW\0", (short)15, (short)82, '1', "\r\n");
for(byte b : packet){
System.out.println(b);
}
}
public static byte[] createMsg(String sMark, short packetle, short cmd, char cErrorCode, String sEnd){
byte[] buffer = new byte[13];
char[] sMarkArr = sMark.toCharArray();
char[] sEndArr = sEnd.toCharArray();
// sMark
buffer[0] = (byte) sMarkArr[0];
buffer[1] = (byte) sMarkArr[1];
buffer[2] = (byte) sMarkArr[2];
buffer[3] = (byte) sMarkArr[3];
buffer[4] = (byte) sMarkArr[4];
buffer[5] = (byte) sMarkArr[5];
// packetle
buffer[6] = (byte)(packetle & 0xFF);
buffer[7] = (byte)((packetle >> 8) & 0xFF);
// cmd
buffer[8] = (byte)(cmd & 0xFF);
buffer[9] = (byte)((cmd >> 8) & 0xFF);
// cErrorCode
buffer[10] = (byte)(cErrorCode);
// sEnd
buffer[11] = (byte)sEndArr[0];
buffer[12] = (byte)sEndArr[1];
return buffer;
}
}
Remember when doing comms in java, the datatypes sizes and format aren't always the same as some other languages.
Check out http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html to get each of the sizes
Hope this helps
EDIT:
Packet is 13 * 8 = 104 bits = 000000000000000000000000000000000000...
calling buffer[0] = (byte)sMark adds the char (e.g. A)
Packet is 13 * 8 = 104 bits = 001000001000000000000000000000000000...
calling buffer[6] = (byte)packetle adds the char (e.g. 2)
Packet is 13 * 8 = 104 bits =
[ Char 6 bytes ][Short 1 byte]
[010000010000000000000000000000000000000000000000][00000010]...
NOTE: as you are using bytes for chars you will need to add an extra byte, as java chars are 16-bit, otherwise you may get loss of data
I have searched and searched and everything I have found has been helpful but I keep getting an out of memory error. The images I send are .06 MB so I know the problem isn't from decoding the byte[] into a bitmap. When I remove the while loops this works like a charm for one frame but I want multiple frames. I am getting a byte[] and sending it to a different device using sockets but I am at a loss how to do this. My problem is that I don't send and receive the correct byte[] length. This is what i am doing currently.
while (count != -1) {
//first send the byte[] length
dataOutputStream.writeInt(sendPackage.length);
//pass a byte array
publishProgress("sending file to client");
showMyToastOnUiThread(String.valueOf(sendPackage.length));
outputStream.write(sendPackage, 0, sendPackage.length);
outputStream.flush();
}
Receive byte[] on different device:
int count = inputStream.read();
while (count != -1) {
int byteArrayLength = dataInputStream.readInt();
Log.i(MainActivity.TAG, "Starting convert to byte array");
byte[] receivedBytes = convertInputStreamToByteArray(inputStream, byteArrayLength);
Bitmap bitmap = BitmapFactory.decodeByteArray(receivedBytes, 0, receivedBytes.length);
publishProgress(bitmap);
}
//convert inputstream to byte[]
public byte[] convertInputStreamToByteArray(InputStream inputStream, int readLength) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] data = new byte[readLength];
try {
Log.i(MainActivity.TAG, "Starting convert to byte array while loop");
int readTotal = 0;
int count = 0;
while (count >= 0 && readTotal < readLength) {
count = inputStream.read(data, readTotal, readLength - readTotal);
if (readLength > 0) {
readTotal += count;
}
}
Log.i(MainActivity.TAG, "Finished convert to byte array while loop");
} catch (IOException e) {
Log.e(MainActivity.TAG, "error: " + e.getMessage());
e.printStackTrace();
}
return data;
}
This is the problem:
int count = inputStream.read();
while (count != -1) {
You're consuming a byte and then ignoring it. That means the next value you read (the size) will be incorrect. You need a different way of telling whether you're at the end of the stream. Some options:
Send a -1 when you're finished; that way you can stop as soon as readInt returns -1
If you know it, send the number of images you're going to send before you start sending them
Use mark(1), then read(), then reset() - if your stream supports marking. I don't know whether it will or not. You could always wrap it in BufferedInputStream if not.
Reimplement DataInputStream.readInt yourself in a way which detects the end of the stream as being an expected possibility instead of throwing an exception
Just catch an exception in readInt (not nice - getting to the end of the stream isn't really exceptional)