I'm writing a simple client-server application and I wanted to be able to take the attributes of a Header class, turn them into a byte[], send them to the other host, and then convert them back into an easily parsed Header. I was using a ByteArrayOutputStream to do this, but the results were not what I expected. For example, just to test it in main() I had:
Header h = Header();
h.setSource(111);
h.setDest(222);
h.setSeq(333);
h.setAck(444);
byte[] header = Header.convertHeaderToByteArray();
Header newHeader = new Header(headerArray);
Where convertHeaderToByteArray() looked like:
public byte[] convertHeaderToByteArray() {
byte[] headerArray;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(44);
byteStream.write(srcPort);
byteStream.write(dstPort);
byteStream.write(seqNum);
byteStream.write(ackNum);
byteStream.write(controlBits);
headerArray = byteStream.toByteArray();
return headerArray;
}
And the Header(headerArray) constructor:
public Header(byte[] headerArray) {
ByteArrayInputStream header = new ByteArrayInputStream(headerArray);
srcPort = header.read();
dstPort = header.read();
seqNum = header.read();
ackNum = header.read();
}
This definitely did not behave as expected. When I looked at those values at the end, srcPort was correct (111), dstPort was correct (222), seqNum was not correct (77), and ackNum was not correct (188).
After hours of reading and tinkering I couldn't get it right, so I tried to use ByteBuffer instead. Viola, correct results.
What is going on here? I read the documentation for both and although I spotted some differences I'm not seeing what the source of my error is.
OutputStream.write(int) writes a single byte. See the Javadoc. If you want to write wider values, you will have to use the writeXXX() methods of DataOutputStream, and the corresponding readXXX() methods of DataInputStream to read them.
Related
In BluetoothChat source code i cannot understand some part of the code-
private void sendMessage(String message) {
// Check that we're actually connected before trying anything
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
// Check that there's actually something to send
if (message.length() > 0) {
// Get the message bytes and tell the BluetoothChatService to write
byte[] send = message.getBytes();
mChatService.write(send);
// Reset out string buffer to zero and clear the edit text field
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
}
}
Here, I undersatnd that byte[] send is a array but cannot understand why i am intializing this array = message.getBytes();
May be its a very silly question but as i am a beginner so i think i should clear this part.
Java experts need your suggestion.
The 'send' has to be a byte array as the mChatService.write() method accepts byte array.
you may read a bit more on the following question:
Java Byte Array to String to Byte Array
The chat service sends binary data, the bytes.
In java text (String, char, Reader/Writer) is a black box of Unicode text, so one may combine all kinds of scripts and languages.
To get the bytes for a specific encoding one does:
String s = "...";
byte[] b = s.getBytes(s, encoding);
Those bytes are in that given encoding.
and reversed:
s = new String(b, encoding);
The version of String.getBytes() without encoding can cause an error: it uses the default, platform encoding, which differs per computer.
Best would have been to return bytes in a Unicode format, like UTF-8.
byte[] b = s.getBytes(StandardCharsets.UTF_8);
I'm wondering what the objections are to using what I'll call the 'String constructor method' to convert an InputStream into a String.
Edit: added emphasis. In particular, I'm wondering why we have to mess with Streams and Buffers and Scanners and whatnot when this method seems to work fine.
private String readStream(InputStream in) {
byte[] buffer = new byte[1024];
try {
return new String(buffer, 0, in.read(buffer));
} catch (IOException e) {
Log.d(DEBUG_TAG, "Error reading input stream!");
return "";
}
}
I've seen this other helpful post and tried the methods I could:
Method 1, Apache commons, is a no-go, since I can't use and don't want libraries right now.
Method 2, The Scanner one, looks promising, but then you'd have to be able to set delimiters in the stream, which isn't always possible, right? E.g. right now I'm using an InputStream from a web API.
Method 3, the InputStreamReader in the slurp function, didn't work either - it gives me a bunch of numbers, where I'm sending a string with all types of characters, so I may be messing something up in my encoding.
But after many Google searches, I finally found the String constructor method, which is the only one that works for me.
From comments on the thread I linked, I know there are issues with encoding in the method I'm using. I've been coding for a while now and know what encodings are and why they're around. But I still lack any knowledge about what kinds of encodings are used where, and how to detect and handle them. Any resources/help on that topic would also be very appreciated!
Here is one method using only standard libraries:
use a ByteArrayOutputStream and copy all the bytes you receive in it;
wrap this ByteArrayOutputStream's bytes into a ByteBuffer;
use a CharsetDecoder to decode the ByteBuffer into a CharBuffer;
.toString() the CharBuffer after rewinding it.
Code (note: doesn't handle closing the input):
// Step 1: read all the bytes
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] buffer = new byte[8196];
int count;
while ((count = in.read(buffer)) != -1)
out.write(buf, 0, count);
// Step 2: wrap the array
final ByteBuffer byteBuffer = ByteBuffer.wrap(out.toByteArray());
// Step 3: decode
final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onUnmappableCharacter(CodingErrorAction.REPORT)
.onMalformedInput(CodingErrorAction.REPORT);
final CharBuffer charBuffer = decoder.decode(byteBuffer);
charBuffer.flip();
return charBuffer.toString();
Here is a sample code demonstrating the problem.
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CodedOutputStream cos = CodedOutputStream.newInstance(bos);
cos.writeRawVarint64(25);
cos.flush();
bos.write("something else".getBytes());
System.out.println("size(bos) = " + bos.size()); // This gives 15
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
CodedInputStream cis = CodedInputStream.newInstance(bis);
System.out.println("size(bis) = " + bis.available()); // This gives 15
long l = cis.readRawVarint64();
System.out.println(cis.getTotalBytesRead()); // This gives 1, which is correct
System.out.println("Raw varint64 = " + l); // This gives 25, which is correct
System.out.println("size(bis) = " + bis.available()); // This now gives 0!!
All I am trying to do is to encode a 64 bit integer and add some more data to the payload. I can read the encoded data correctly. But for some reason, it clears the underlying stream after that. Any one know why this is happening? How can I read the varint from stream and read the remaining bytes as indicated by the varint?
Any help would be great
I have no idea what codedinputstream does but it could very well buffer the input meaning it reads e.g. 100 bytes a time.
Either way you should not wrap an inputstream B around an inputstream A and continue reading from A specifically because you don't know what B does.
For instance maybe B must look ahead in the data to form some conclusion or it uses buffering or...
Additional note: available() is usually a bad idea though it should work correctly specifically on a bytearrayinputstream.
EDIT:
In conclusion: just continue reading from the codedinputstream, don't try to read from the underlying one.
I'm working on writing individual bytes to a DataOutputStream in java for an HTTP post request. The post is structured like such:
/* Init Post */
URL PostToLink = new URL(GeneralArguments.get("PostLink_String"));
byte[] PostData = PutKeyedPostArgs.get("PostBody").getBytes("UTF-8");
HttpURLConnection Post_Request_Connection = (HttpURLConnection) PostToLink.openConnection();
Post_Request_Connection.setDoOutput(true);
Post_Request_Connection.setDoInput(false);
Post_Request_Connection.setRequestMethod("POST");
//Post_Request_Connection.setRequestProperty("charset", "utf-8");
Post_Request_Connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
Post_Request_Connection.setRequestProperty("Content-Length", Integer.toString(PostData.length));
Post_Request_Connection.setRequestProperty("Connection", "Keep-Alive");
Post_Request_Connection.setRequestProperty("User-Agent", UserAgent); // Defined earlier
Post_Request_Connection.setRequestProperty("Cookie", CookieVal); // Defined earlier
Post_Request_Connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
Post_Request_Connection.setRequestProperty("Accept-Encoding", "gzip,deflate,sdch");
Post_Request_Connection.setRequestProperty("Accept-Language", "en-US,en;q=0.8");
Post_Request_Connection.setInstanceFollowRedirects(false);
Post_Request_Connection.setUseCaches(false);
/* Obtain Write Stream */
DataOutputStream The_Post_Request_Write_Stream = new DataOutputStream(Post_Request_Connection.getOutputStream());
JOptionPane.showMessageDialog(null, PostData.length); // For Debugging
JOptionPane.showMessageDialog(null, "before for"); // For Debugging
/* Begin writing byte-by-byte to output stream */
for(int CurrentPostByte = 0; CurrentPostByte < PostData.length; CurrentPostByte++){
JOptionPane.showMessageDialog(null, CurrentPostByte); // For Debugging
byte[] TemporaryByteArray = new byte[]{PostData[CurrentPostByte]};
The_Post_Request_Write_Stream.write(TemporaryByteArray, CurrentPostByte, TemporaryByteArray.length);
/* Length should always be 1 */
}
For some reason it after it writes the second byte (the one at PostData[1]) it gets an index out of bounds error. I cannot seem to find out why.
Any clarifications or help is appreciated. Thank you.
Look at your code:
byte[] TemporaryByteArray = new byte[]{PostData[CurrentPostByte]};
The_Post_Request_Write_Stream.write(TemporaryByteArray, CurrentPostByte, TemporaryByteArray.length);
You are passing the array TemporaryByteArray which has a length of 1 (one) to the write method but use the indices valid for the PostData array only.
You may fix the code by changing it to:
byte[] TemporaryByteArray = new byte[]{PostData[CurrentPostByte]};
The_Post_Request_Write_Stream.write(TemporaryByteArray, 0, TemporaryByteArray.length);
or, simpler
byte[] TemporaryByteArray = new byte[]{PostData[CurrentPostByte]};
The_Post_Request_Write_Stream.write(TemporaryByteArray, 0, 1);
or, even simpler
The_Post_Request_Write_Stream.write(PostData, CurrentPostByte, 1);
But, of course, the best solution would be removing the nonsense loop and write the entire array at once, The_Post_Request_Write_Stream.write(PostData); instead of byte by byte.
My educated guesstimate is that the problem is here:
The_Post_Request_Write_Stream.write(TemporaryByteArray, CurrentPostByte, TemporaryByteArray.length);
The parameters to write(byte[], int, int) are:
a byte[] buffer which contains the data to be written
an int offset that tells where in the buffer the data you want starts
an int length which indicates how many bytes you want to write
The problem lies in the value you pass to the offset parameter. It's CurrentPostByte, so in the second iteration you tell it to start reading TemporaryByteArray from index 1. However, TemporaryByteArray is always a one-element array, it only has one item int it at index 0.
You should correct that to:
The_Post_Request_Write_Stream.write(TemporaryByteArray, 0, TemporaryByteArray.length);
Or, more simply, to:
The_Post_Request_Write_Stream.write(TemporaryByteArray);
You didn't say which line is throwing the exception, but I'm guessing it's
byte[] TemporaryByteArray = new byte[]{BuyData[CurrentPostByte]};
Looks like you meant to reference PostData here.
I know how to get the inputstream for a given classpath resource, read from the inputstream until i reach the end, but it looks like a very common problem, and i wonder if there an API that I don't know, or a library that would make things as simple as
byte[] data = ResourceUtils.getResourceAsBytes("/assets/myAsset.bin")
or
byte[] data = StreamUtils.readStreamToEnd(myInputStream)
for example!
Java 9 native implementation:
byte[] data = this.getClass().getClassLoader().getResourceAsStream("/assets/myAsset.bin").readAllBytes();
Have a look at Google guava ByteStreams.toByteArray(INPUTSTREAM), this is might be what you want.
Although i agree with Andrew Thompson, here is a native implementation that works since Java 7 and uses the NIO-API:
byte[] data = Files.readAllBytes(Paths.get(this.getClass().getClassLoader().getResource("/assets/myAsset.bin").toURI()));
Take a look at Apache IOUtils - it has a bunch of methods to work with streams
I usually use the following two approaches to convert Resource into byte[] array.
1 - approach
What you need is to first call getInputStream() on Resource object, and then pass that to convertStreamToByteArray method like below....
InputStream stream = resource.getInputStream();
long size = resource.getFile().lenght();
byte[] byteArr = convertStreamToByteArray(stream, size);
public byte[] convertStreamToByteArray(InputStream stream, long size) throws IOException {
// check to ensure that file size is not larger than Integer.MAX_VALUE.
if (size > Integer.MAX_VALUE) {
return new byte[0];
}
byte[] buffer = new byte[(int)size];
ByteArrayOutputStream os = new ByteArrayOutputStream();
int line = 0;
// read bytes from stream, and store them in buffer
while ((line = stream.read(buffer)) != -1) {
// Writes bytes from byte array (buffer) into output stream.
os.write(buffer, 0, line);
}
stream.close();
os.flush();
os.close();
return os.toByteArray();
}
2 - approach
As Konstantin V. Salikhov suggested, you could use org.apache.commons.io.IOUtils and call its IOUtils.toByteArray(stream) static method and pass to it InputStream object like this...
byte[] byteArr = IOUtils.toByteArray(stream);
Note - Just thought I'll mention this that under the hood toByteArray(...) checks to ensure that file size is not larger than Integer.MAX_VALUE, so you don't have to check for this.
Commonly Java methods will accept an InputStream. In that majority of cases, I would recommend passing the stream directly to the method of interest.
Many of those same methods will also accept an URL (e.g. obtained from getResource(String)). That can sometimes be better, since a variety of the methods will require a repositionable InputStream and there are times that the stream returned from getResourceAsStream(String) will not be repositionable.