I'm trying to implement a method which provides data encryption using Object Serialization through Sockets (ObjectInputStream && ObjectOutputStream are used).
The aim here is to reduce everything to an array of byte data, which will be used as input for an encryption algorithm.
Here is a very rude code which i wrote to simply test and see how things work:
List<Byte> bytes=new LinkedList<>();
try (
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
InputStream finalInputStream=new InputStream() {
int counter=0;
#Override
public int read() throws IOException {
if (counter<bytes.size()) {
return bytes.get(counter++);
}
else return -1;
}
};
OutputStream finalOutputStream=new OutputStream() {
#Override
public void write(int b) throws IOException {
bytes.add((byte) b);
}
};
BufferedOutputStream bfos=new BufferedOutputStream(finalOutputStream);
BufferedInputStream bios=new BufferedInputStream(finalInputStream);
ObjectInputStream ois=new ObjectInputStream(bios);
){
oos.writeObject(new CryptoMain());
oos.flush();
bfos.write(bos.toByteArray());
CryptoMain obj=(CryptoMain)ois.readObject();
obj.printHello();
}
catch(Exception e) {
e.printStackTrace();
}
However i get EOFException at this statement:
ObjectInputStream ois=new ObjectInputStream(bios);
How can i obtain what i want? Is there an other way?
Thank you.
You don't need most of this.
You don't need both the ByteArrayOutputStream and the List<Byte> and the local InputStream class. You're doing everything three times. You don't actually need any of them.
There are several simple solutions:
javax.crypto.SealedObject
javax.crypto.CipherInput/OutputStream.
TLS.
Related
I have a function which writes the given input stream to a given output stream. Code below.
static void copyStream(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[4096];
int len;
try {
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
}
The above function is called from this function
public static void copyFile(File srcFile, File destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
try {
FileOutputStream fos = new FileOutputStream(destFile);
try {
**copyStream**(fis, fos);
} finally {
if (fos != null)
fos.close();
}
} finally {
if (fis != null)
fis.close();
}
}
In this function, I am writing 4 MB at once. I use this function to copy images. Occasionally I see that the destination file is not created due to which an exception occurs while trying to read that file for future processing. I am guessing the culprit to be not closing the resources. Is my hypothesis good? What are the reasons why my function might fail? Please help
I believe, that given InputStream and OutputStream installed correctly.
Add os.flush(); at the end. Sure, both streams should be closed in the caller as well.
As alternative, you could use Apache IO utils org.apache.commons.io.IOUtils.copy(InputStream input, OutputStream output).
Yes you absolutely must close your destination file to ensure that all caches from the JVM through to the OS are flushed and the file is ready for a reader to consume.
Copying large files the way that you are doing is concise in code but inefficient in operation. Consider upgrading your code to use the more efficient NIO methods, documented here in a blog post. In case that blog disappears, here's the code:
Utility class:
public final class ChannelTools {
public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
// prepare the buffer to be drained
buffer.flip();
// write to the channel, may block
dest.write(buffer);
// If partial transfer, shift remainder down
// If buffer is empty, same as doing clear()
buffer.compact();
}
// EOF will leave buffer in fill state
buffer.flip();
// make sure the buffer is fully drained.
while (buffer.hasRemaining()) {
dest.write(buffer);
}
}
}
Usage example with your InputStream and OutputStream:
// allocate the stream ... only for example
final InputStream input = new FileInputStream(inputFile);
final OutputStream output = new FileOutputStream(outputFile);
// get an channel from the stream
final ReadableByteChannel inputChannel = Channels.newChannel(input);
final WriteableByteChannel outputChannel = Channels.newChannel(output);
// copy the channels
ChannelTools.fastChannelCopy(inputChannel, outputChannel);
// closing the channels
inputChannel.close();
outputChannel.close()
There is also a more concise method documented in Wikipedia that achieves the same thing with less code:
// Getting file channels
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(target).getChannel();
// JavaVM does its best to do this as native I/O operations.
in.transferTo(0, in.size(), out);
// Closing file channels will close corresponding stream objects as well.
out.close();
in.close();
This question already has answers here:
Easy way to write contents of a Java InputStream to an OutputStream
(24 answers)
Closed 6 years ago.
In my method, I'm saving data from file to an output stream.
For now, it looks like this
public void readFileToOutputStream(Path path, OutputStream os) {
byte[] barr = Files.readAllBytes(path)
os.write(barr);
os.flush();
}
But in this solution, all bytes are loaded into memory, and I want to use buffer to release some of it.
What can I use to supply my reading with buffer?
easist way is to use Commons IO library
public void readFileToOutputStream(Path path, OutputStream os) throws IOException {
try(InputStream in = new FileInputStream(path.toFile())){
IOUtils.copy(in, os);
}
}
You can implement on your own similar to IOUtils.copy
public void readFileToOutputStream(Path path, OutputStream os) throws IOException {
try (InputStream fis = new FileInputStream(path.toFile());
InputStream bis = new BufferedInputStream(fis)) {
byte[] buffer = new byte[4096];
int n;
while ((n = bis.read(buffer)) >= 0) {
os.write(buffer, 0, n);
}
}
}
If i understand your question right, you would like to only write a specified amount of bytes to memory?
outputstreams write method can also write a specified byte array from a starting offset and length.
https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html
public void readFileToOutputStream(Path path, OutputStream os, int off, int len) {
byte[] barr = Files.readAllBytes(path)
os.write(barr, off, len);
os.flush();
}
Use buffered streams to manage the buffer for you:
public void readFileToOutputStream(Path path, OutputStream os) {
try (FileInputStream fis = new FileInputStream(path.toFile())) {
try (BufferedInputStream bis = new BufferedInputStream(fis)) {
try (DataInputStream dis = new DataInputStream(bis)) {
try (BufferedOutputStream bos = new BufferedOutputStream(os)) {
try (DataOutputStream dos = new DataOutputStream(bos)) {
try {
while (true) {
dos.writeByte(dis.readByte());
}
} catch (EOFException e) {
// normal behaviour
}
}
}
}
}
}
}
Use FileInputStream::read(byte[] b, int off, int len) link
to read up to len bytes to buffer b and FileOutputStream::write(byte[] b, int off, int len) link2 to write from buffer
This is my code. When I run this, I get up to "Three and a Half" printed. (The prints are added for debugging since I don't know any other way.) After that, the execution hangs. No exceptions, no prompts, nothing. So what is wrong with my object creation? Each and every tutorial I see online has the same code, but mine won't work.
public class Connection {
Socket socket;
ObjectInputStream iStream;
ObjectOutput outputStream;
public Connection(Socket s) {
try {
System.out.println("One");
socket = s;
System.out.println("Two");
outputStream = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
System.out.println("Three");
InputStream is = socket.getInputStream();
System.out.println("Three and a Half");
iStream = new ObjectInputStream(is);
System.out.println("Four");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Thanks in advance.
It's in the Javadoc:
A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header.
So the new ObjectInputStream is hanging because it's waiting on input. You need to create an ObjectOutputStream and send data through the socket.
On server (C++), binary data is compressed using ZLib function:
compress2()
and it's sent over to client (Java).
On client side (Java), data should be decompressed using the following code snippet:
public static String unpack(byte[] packedBuffer) {
InflaterInputStream inStream = new InflaterInputStream(new ByteArrayInputStream( packedBuffer);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
int readByte;
try {
while((readByte = inStream.read()) != -1) {
outStream.write(readByte);
}
} catch(Exception e) {
JMDCLog.logError(" unpacking buffer of size: " + packedBuffer.length);
e.printStackTrace();
// ... the rest of the code follows
}
Problem is that when it tries to read in while loop it always throws:
java.util.zip.ZipException: invalid stored block lengths
Before I check for other possible causes can someone please tell me can I compress on one side with compress2 and decompress it on the other side using above code, so I can eliminate this as a problem? Also if someone has a possible clue about what might be wrong here (I know I didn't provide too much of of the code in here but projects are rather big.
Thanks.
I think the problem is not with unpack method but in packedBuffer content. Unpack works fine
public static byte[] pack(String s) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream dout = new DeflaterOutputStream(out);
dout.write(s.getBytes());
dout.close();
return out.toByteArray();
}
public static void main(String[] args) throws Exception {
byte[] a = pack("123");
String s = unpack(a); // calls your unpack
System.out.println(s);
}
output
123
public static String unpack(byte[] packedBuffer) {
try (GZipInputStream inStream = new GZipInputStream(
new ByteArrayInputStream(packedBuffer));
ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
inStream.transferTo(outStream);
//...
return outStream.toString(StandardCharsets.UTF_8);
} catch(Exception e) {
JMDCLog.logError(" unpacking buffer of size: " + packedBuffer.length);
e.printStackTrace();
throw new IllegalArgumentException(e);
}
}
ZLib is the zip format, hence a GZipInputStream is fine.
A you seem to expect the bytes to represent text, hence be in some encoding, add that encoding, Charset, to the conversion to String (which always holds Unicode).
Note, UTF-8 is the encoding of the bytes. In your case it might be an other encoding.
The ugly try-with-resources syntax closes the streams even on exception or here the return.
I rethrowed a RuntimeException as it seems dangerous to do something with no result.
I'm doing some socket programming in Java and I'd like to be able to change between using the ObjectOutputStream, the DataOutputStream, and the PrintWriter all within the same socket/connection. Is this possible and what is the best way to do it?
I've tried just creating both types of objects, for example ObjectOutputStream and DataOutputStream, but that doesn't seem to work.
The reason I want to switch between them is to, for example, send a text command "INFO" that signals I'm about to send an object with information or a command "DATA" signalling that I'm about to send data. Any advice on the best way to do this is appreciated.
You can only use one underlying stream type however you can get that data from anywhere.
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
public static void writeObject(DataOutputStream dos, Serializable obj) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
dos.writeUTF("OBJECT");
byte[] bytes = baos.toByteArray();
dos.writeInt(bytes.length);
dos.write(bytes);
dos.flush();
}
public static void writeBytes(DataOutputStream dos, byte[] bytes) {
dos.writeUTF("BYTES");
dos.writeInt(bytes.length);
dos.write(bytes);
dos.flush();
}
public static void writeText(DataOutputStream dos, String text) {
dos.writeUTF("TEXT");
dos.writeUTF(text);
dos.flush();
}
Why do you want the *Stream to convert to the *Writer.
You can do what you want to do with *Stream.
Socket s = new Socket();
DataOutputStream stream = new DataOutputStream( s.getOutputStream() );
byte[] bytes = "INFO".getBytes();
stream.write(bytes);
//....
bytes = "DATA".getBytes();
stream.write(bytes);