I want to compress my serializable objects and send them through java sockets.
serializable object:
public class MyObject implements Serializable {
private int id;
private String name;
//getter,setter and equal, hashCode, toString methods
}
public class Sender {
private Socket clientSocket;
public void send(MyObject obj){
GZIPOutputStream gzipOut = new GZIPOutputStream(clientSocket.getOutputStream());
ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut);
objectOut.writeObject(obj);
}
}
public class Receiver {
private Socket serverSocket;
public MyObject receive(){
GZIPInputStream gzipIn = new GZIPInputStream(serverSocket.getInputStream());
ObjectInputStream objectIn = new ObjectInputStream(gzipIn);
return (MyObject) objectIn.readObject();
}
}
this throws ZipException: Not in GZIP format at receiver side.
I won't close streams and sockets, because need to send multiple objects using the same stream and socket (performance wise)
ObjectStream is one of the most verbose serialisation formats available. Just about any other Serialization would be better for size. ObjectStreams have to flushed to send all the data and you can't send more than one stream in a connection, making it inefficient to just send one object per connection.
Compressed streams work best for large amounts of data, when you try to apply them to a few bytes, the result is larger, not smaller. Compressed Streams must be flushed to be read.
need to send multiple objects using the same stream and socket (performance wise)
This means you have to retain your GZIP/Object streams. You can't use more than one per stream.
public class ObjectSocket implements Closeable {
private final Socket socket;
private final ObjectOutputStream output;
private final ObjectInputStream input;
public ObjectSocket(Socket socket) throws IOException {
this.socket = socket;
this.output = new ObjectOutputStream(new DeflaterOutputStream(socket.getOutputStream()));
this.input = new ObjectInputStream(new InflaterInputStream(socket.getInputStream()));
}
public void send(Serializable obj) throws IOException {
output.writeObject(obj);
output.reset();
output.flush();
}
public <T extends Serializable> T receive() throws IOException, ClassNotFoundException {
return (T) input.readObject();
}
#Override
public void close() throws IOException {
output.close();
input.close();
socket.close();
}
}
Related
I created a simple server and a client, but the server could not read anything that was sent from the client. I also add a print statement after I sent the string, but it cannot be printed either.
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(6666);
Socket clientSocket = serverSocket.accept();
System.out.println("accepting client at address " + clientSocket.getRemoteSocketAddress());
ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
String input = (String) in.readObject();
System.out.println(input);
out.writeObject("Received");
out.flush();
}
}
Below is the client, and I just want to send a string "?????does not send":
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Client client = new Client();
client.sentInfo();
}
private static class Client {
private ObjectInputStream objectInputStream;
private ObjectOutputStream objectOutputStream;
public Client() throws IOException {
Socket socket = new Socket("127.0.0.1", 6666);
this.objectInputStream = new ObjectInputStream(socket.getInputStream());
this.objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
}
public void sentInfo() throws IOException, ClassNotFoundException {
this.objectOutputStream.writeObject("?????does not send");
this.objectOutputStream.flush();
System.out.println("????????");
Message resp = (Message) this.objectInputStream.readObject();
System.out.println(resp.getMessage());
}
}
}
I tried something else, if I just use InputStream and use a buffer to read bytes, like this:
Server code
This is the client code: client code
The code in the two link above would work. However, it would not work if I tried to use ObjectInputStream:
This is the server: server
This is the client: client
This is the Message object I want to send: Message class
Can someone explain this for me please? Thanks!
To read Strings from a socket use something like this:
DataInputStream input = new DataInputStream(clientSocket.getInputStream());
String message = input.readUTF();
You can open multiple streams from a socket, so if you want to read something else that really needs the ObjectInputStream than it can be open as well. Don't forget to properly close the streams & sockets.
I have this method to deserialize:
public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
Object res = is.readObject();
is.close();
in.close();
return res;
}
and this one to serialize:
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
byte[] res = out.toByteArray();
out.close();
os.close();
return res;
}
I use these methods to serialize and deserialize a class object, that only has a string and an arrayList of another class, exchanged between 2 devices. Both the class of the object and the class of the arrayList implement serializable.
When I send an object with up to 3 elements in the arrayList these methods work perfectly. However, when the arrayList has 4 or more elements, the device receiving the object still detects that some data has "arrived" but the deserialize method generates an "EOFException" in the "Object res = is.readObject();" line.
Any ideas about what the problem could be ?
EDIT
This is the class of the arrayList:
import java.io.Serializable;
public class Info implements Serializable {
public Info() {
...
}
...
}
This is the class of the object:
import java.io.Serializable;
import java.util.ArrayList;
public class BluetoothDataContainer implements Serializable{
private ArrayList<Info> dataList;
private String originDevice;
public BluetoothDataContainer(String originDevice){
dataList= new ArrayList<Info>();
this.originDevice = originDevice;
}
...
}
This is the code I use to send the object:
BluetoothDataContainer data = new BluetoothDataContainer(mBluetoothAdapter.getName());
...
// add needed info to variable 'data'
...
s.write(data);
Where 's' is a thread with the method 'write':
private BluetoothSocket mmSocket = bluetoothDevice.createRfcommSocketToServiceRecord(ID_CONNECTION);
private OutputStream mmOutStream = mmSocket.getOutputStream();
...
public void write(BluetoothDataContainer m) {
try {
mmOutStream.write(serialize(m));
} catch (IOException e) {
this.mContext.showToast("IOException caught in thread ConnectedThread [Bluetooth connection handler] - write() !");
}
//cancel();
this.interrupt();
}
And this is how I handle the object when it is read:
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
final BluetoothDataContainer data;
try {
data = (BluetoothDataContainer) deserialize(readBuf);
...
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
};
And this is how I read the object:
private final Handler mHandler; // value set in the constructor
...
public void run() {
mmInStream = bluetoothSocket.getInputStream();
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes;
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
this.mContext.showToast("ConnectedThread [Bluetooth connection handler] data received !");
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(1, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
this.mContext.showToast("IOException caught in thread ConnectedThread [Bluetooth connection handler] - run() !");
}
}
Clearly you have not 'exchanged' the entire byte array in the case that fails.
bytes = mmInStream.read(buffer);
You can't possibly know from this alone whether you've read:
an entire message
less than one message
more than one message.
As you already have a socket with input and output streams, it beats me why you are creating byte arrays at all. Just wrap the ObjectOutputStream around the socket output stream and use writeObject(). At the receiver, wrap the ObjectInputStream around the socket input stream and use readObject().
NB you should use the same object streams for the life of the socket, and if you are sending objects both ways you must create the object output stream before the object input stream for the same socket: otherwise you can get a deadlock.
I am a student and learning Network Programming and have a some problem.
This is my client:
public class Test2Client_Tranfer_An_Obj {
Socket socket = null;
ObjectOutputStream out;
ObjectInputStream in;
public Test2Client_Tranfer_An_Obj() {
try {
socket = new Socket("localhost", 12345);
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
System.out.println("Ready");
System.out.println("" + in.readUTF());
System.out.println("" + in.readUTF());
System.out.println("Recived");
out.writeUTF("hihi");
System.out.println("Sended");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("Client");
Test2Client_Tranfer_An_Obj test = new Test2Client_Tranfer_An_Obj();
}
}
This my Server:
public class Test2Server_Tranfer_An_Obj {
ServerSocket serverSocket;
ObjectOutputStream out;
ObjectInputStream in;
public Test2Server_Tranfer_An_Obj() {
try {
serverSocket = new ServerSocket(12345);
Socket socket = serverSocket.accept();
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
System.out.println("Ready!");
out.writeUTF("huhu");
out.writeUTF("hoho");
System.out.println("Sended");
String s = in.readUTF();
System.out.println("" + s);
System.out.println("Recived");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("Server");
Test2Server_Tranfer_An_Obj demo = new Test2Server_Tranfer_An_Obj();
}
}
But, when i run my program, this result:
Server Console
Server
Ready!
Sended
Client Console
Client Ready
Anybody can tell me why and what i can do?
Thank for reading!
Hope recive you answer
Object Stream is overkill in this case. You are not actually using writeObject/readObject and using DataInputStream and DataOutputStream would do what you want.
In this particular case, an Object Stream is buffered, which means that small writes are buffered until you either flush() or close() the stream to improve performance. As you don't do either, the writeUTF only writes to memory, not the Socket.
c.f. Data Streams are not buffered by default.
In your server after write to the outputstream. you have to add out.flush() to write to socket
I'm creating an application which will need to transmit data back and forth between multiple computers on a network. Because of the way the data is to be sent, the client computers will be running the socket server, and the coordinating computer will be running the client socket.
I've created simple classes which are simply intended to encapsulate reading from and writing to these sockets. However, instead of reading anything, the receiving socket simply outputs nothing. I have confirmed that both client and server have a connection.
In the following Server and Client classes, the Socket is made public for debugging purposes only.
public class Server {
public Socket client;
private DataInputStream inStr;
private PrintStream outStr;
public Server() throws UnknownHostException, IOException {this("localhost");}
public Server(String hostname) throws UnknownHostException, IOException {
client = new Socket(hostname, 23);
inStr = new DataInputStream(client.getInputStream());
outStr = new PrintStream(client.getOutputStream());
}
public void send(String data) {outStr.print(data); outStr.flush();}
public String recv() throws IOException {return inStr.readUTF();}
}
The following is my Client:
public class Client {
private ServerSocket serv;
public Socket servSock;
private DataInputStream inStr;
private PrintStream outStr;
public Client() throws IOException {
serv = new ServerSocket(23);
servSock = serv.accept();
inStr = new DataInputStream(servSock.getInputStream());
outStr = new PrintStream(servSock.getOutputStream());
}
public void send(String data) {outStr.print(data); outStr.flush();}
public String recv() throws IOException {return inStr.readUTF();}
}
The Client class is instantiated, and the program started. Then, in a separate program, the Server is instantiated and started:
Server s = new Server(); System.out.println(s.client.isConnected());
while(true) {System.out.println(s.recv()); Thread.sleep(200);}
Client c = new Client(); System.out.println(c.servSock.isConnected());
while(true) {c.send("Hello World!"); Thread.sleep(200);}
isConnected() returns true for both the Client and the Server.
What could be causing this? I've never had to use sockets before now.
DataInputStream.readUTF() expects the first two bytes to be the number of bytes to read, but PrintStream.print(String) will convert the string to bytes and write them as-is.
DataOutputStream.writeUTF(String) will write the length like readUTF() expects.
Why does this program not work properly? Client reads SOME_MESSAGE and after that nothing happens. It seems that println method from server in some way have influence on transferring long type numbers.
SERVER
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket socket = new ServerSocket(9999);
while (true) {
Socket sock = socket.accept();
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream())), true);
DataOutputStream outByte = new DataOutputStream(sock.getOutputStream());
out.println("SOME_MESSAGE");
outByte.writeLong(948L);
}
}
}
CLIENT
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) throws Exception {
Socket sock = new Socket("127.0.0.1", 9999);
DataInputStream inByte = new DataInputStream(sock.getInputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(
sock.getInputStream()));
System.out.println(in.readLine());
long number = inByte.readLong();
System.out.println(number);
}
}
Your problem is that the BufferedReader is buffering bytes from the socket's input stream, so the long 948 value isn't in the DataInputStream because the BufferedReader has it read and is buffering it. In general you don't want to be using 2 different wrappers around the same underlying stream, especially if one is buffered. Same with your Server class, but that seems to at least be working.
Your Client needs to use only one wrapper for the socket's input stream. You should just stick with the DataInputStream and along with the Server code, use DataInputStream.readUTF() on the Client while using DataOutputStream.writeUTF() on the Server, getting rid of both the BufferedReader and the PrintWriter.
So on the Server:
while(true) {
Socket sock = socket.accept();
DataOutputStream outByte = new DataOutputStream(sock.getOutputStream());
outByte.writeUTF("SOME_MESSAGE");
outByte.writeLong(948L);
outByte.flush();
}
and on the Client:
public static void main(String[]args)throws Exception
{
Socket sock = new Socket("127.0.0.1",9999);
DataInputStream inByte = new DataInputStream(sock.getInputStream());
System.out.println(inByte.readUTF());
long number = inByte.readLong();
System.out.println(number);
}
It is interesting that java does not allow to do that having said that there is always a better solution. You can use serialization to do your job.
create a interface PayLoad like below
import java.io.Serializable;
public interface PayLoad extends Serializable
{
String getMessage();
//Java does not allow to define byte array of long
int getLength();
byte[] getbytes();
}
Then create an Implementation class like below
public class FilePayLoad implements PayLoad
{
private final String message;
private final int length;
private final byte[] bytes;
public FilePayLoad(String message, int length, byte[] bytes)
{
this.message = message;
this.length = length;
this.bytes = bytes;
}
#Override
public String getMessage()
{
return this.message;
}
#Override
public int getLength()
{
return this.length;
}
#Override
public byte[] getbytes()
{
return this.bytes;
}
}
Now change your server and client like below
Server
public class Server
{
public static void main(String[] args) throws Exception
{
ServerSocket socket = new ServerSocket(9999);
while (true)
{
Socket sock = socket.accept();
ObjectOutputStream out = new ObjectOutputStream(sock.getOutputStream());
byte[] bytes = "SOME_MESSAGE".getBytes();
out.writeObject(new FilePayLoad("SOME_MESSAGE", bytes.length, bytes));
out.flush();
}
}
}
Client
public class Client
{
public static void main(String[] args) throws Exception
{
Socket sock = new Socket("127.0.0.1", 9999);
ObjectInputStream inByte = new ObjectInputStream(sock.getInputStream());
PayLoad payLoad = (PayLoad) inByte.readObject();
System.out.println(payLoad.getMessage());
System.out.println(payLoad.getLength());
System.out.println(new String(payLoad.getbytes()));
}
}