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.
Related
I am doing a Server/Client network. The messages are send as Json Objects.
I have a method for writing the Json-Object but my method for reading the Json-Object doesnt work. I dont have any exceptions but there is no output like excpected. Something must be wrong with the bufferedReader. I dont know how to get that Json-Object from the socket who sent it.
Method for writing:
public void writeMessage(JSONObject json) {
try {
PrintWriter printWriter = new PrintWriter(
new OutputStreamWriter(
socket.getOutputStream()));
printWriter.print(json);
printWriter.flush();
} catch (IOException writeMessageException) {
System.out.println("!");
}
}
method for reading the message/ receiving the message:
private static void readMessageFromServer(Socket socket) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())))
{
StringBuilder sb = new StringBuilder();
String readLine;
while ((readLine = br.readLine()) != null) {
sb.append(readLine);
}
JSONObject js = new JSONObject(sb.toString());
String action1 = (String) js.get("action1");
System.out.println(action1);
} catch(IOException e) {
System.out.println("!");
}
}
Thank you :)
Consider using javax websocket.
javax.websocket client simple example
You can simply implement MessageHandler and specify the type parameter you expecting to receive using POJOs.
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<MyJsonObject>() {
#Override
public void onMessage(MyJsonObject message) {
//do something
}
});
}
That way you are creating a listener (#OnMessage) and handling each message as soon as it received. You can also implement MessageHandler with String type parameter and handle parsing the json yourself inside OnMessage method;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
MyJsonObject jObj = new ObjectMapper().readValue(message, MyJsonObject.class);
}
});
}
If you want to stick with java net socket consider switching to Jackson Object Mapper from JSONObject, that way you can convert input stream into any object:
private static void readMessageFromServer(Socket socket){
InputStream in = socket.getInputStream();
MyObject obj = new ObjectMapper().readValue(in, MyObject.class);
}
more javax webSocket examples
When reading data in bytes, we need to define our own protocol for communication between server and client. The simplest protocol which
we can define is called TLV (Type Length Value). It means that every
*message written to the socket is in the form *of the Type Length Value.
So we define every message sent as:
A 1 byte character that represents the data type, like s for String A
4 byte integer that indicates the length to the data And then the
actual data, whose length was just indicated
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
char dataType = in.readChar();
int length = in.readInt();
if(dataType == 's') {
byte[] messageByte = new byte[length];
boolean end = false;
StringBuilder dataString = new StringBuilder(length);
int totalBytesRead = 0;
while(!end) {
int currentBytesRead = in.read(messageByte);
totalBytesRead = currentBytesRead + totalBytesRead;
if(totalBytesRead <= length) {
dataString
.append(new String(messageByte, 0, currentBytesRead, StandardCharsets.UTF_8));
} else {
dataString
.append(new String(messageByte, 0, length - totalBytesRead + currentBytesRead,
StandardCharsets.UTF_8));
}
if(dataString.length()>=length) {
end = true;
}
}
}
More detail info can be found here
https://www.baeldung.com/java-inputstream-server-socket
I'm developing a real time application in Java with communication between one server and one client with UDP socket.
It worked fine when i send and receive string but when i tried to receive a custom class Message. i got this error :java.io.StreamCorruptedException: invalid stream header: 00000000
at line message = (Message) objectInputStream.readObject(); in the "receiveMessage" function
I don't know why i have this error.
My Message class:
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private TypeMessage typeMessage;
private int seedMap;
public Message(TypeMessage typeMessage) {
this.typeMessage = typeMessage;
}
public TypeMessage getTypeMessage() {
return typeMessage;
}
public void setTypeMessage(TypeMessage typeMessage) {
this.typeMessage = typeMessage;
}
public int getSeedMap() {
return seedMap;
}
public void setSeedMap(int seedMap) {
this.seedMap = seedMap;
}
My send function used by the client and the server:
public final static void sendMessage(Message message, DatagramChannel socketSender, InetSocketAddress addressRecever) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos;
oos = new ObjectOutputStream(baos);
oos.writeObject(message);
oos.flush();
byte[] data = baos.toByteArray();
ByteBuffer bufferSocket = ByteBuffer.allocate(SIZE_BUFFER);
bufferSocket.put(data);
socketSender.send(bufferSocket, addressRecever);
} catch (Exception e) {
System.out.println(e);
}
}
My receive function used by the server and the client:
public final static Message receiveMessage(DatagramChannel socketRecever, SocketAddress socketSender) {
Message message = null;
try {
ByteBuffer bufferSocket = ByteBuffer.allocate(SIZE_BUFFER);
bufferSocket.clear();
socketSender = socketRecever.receive(bufferSocket);
if(socketSender != null) {
bufferSocket.flip();
byte[] bytes = new byte[bufferSocket.limit()];
bufferSocket.get(bytes);
ByteArrayInputStream bytesStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(bytesStream);
message = (Message) objectInputStream.readObject();
}
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
return message;
}
Thanks in advance for help
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();
}
}
I wrote some code for a server and a client to transfer a file from the server to the client and it worked like a charm; however I have few questions. I want to build this code under a GUI, and I want to list all the files on the folder, but how can I make the client choose the file he wants after he see the list of files offered (how can I send a string to the server in order to choose the file)?
Server Code
import java.io.*;
import java.net.*;
class TCPServer {
public static void listfile(){
File folder = new File("c:/");
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
System.out.println("File " + listOfFiles[i].getName());
} else if (listOfFiles[i].isDirectory()) {
System.out.println("Directory " + listOfFiles[i].getName());
}
}
}
public static void main(String args[]) {
listfile();
while (true) {
ServerSocket welcomeSocket = null;
Socket connectionSocket = null;
BufferedOutputStream outToClient = null;
try {
welcomeSocket = new ServerSocket(3248);
connectionSocket = welcomeSocket.accept();
outToClient = new BufferedOutputStream(connectionSocket.getOutputStream());
} catch (IOException ex) {
// Do exception handling
}
if (outToClient != null) {
String FileName = "carexception.java";
File myFile = new File("C:\\"+FileName);
byte[] mybytearray = new byte[(int) myFile.length()];
FileInputStream fis = null;
try {
fis = new FileInputStream(myFile);
} catch (FileNotFoundException ex) {
// Do exception handling
}
BufferedInputStream bis = new BufferedInputStream(fis);
try {
bis.read(mybytearray, 0, mybytearray.length);
outToClient.write(mybytearray, 0, mybytearray.length);
outToClient.flush();
outToClient.close();
connectionSocket.close();
// File sent, exit the main method
return;
} catch (IOException ex) {
// Do exception handling
}
}
}
}
}
Client Code
import java.io.*;
import java.net.*;
import java.util.*;
class TCPClient {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
byte[] aByte = new byte[1];
int bytesRead;
Socket clientSocket = null;
InputStream is = null;
try {
clientSocket = new Socket("127.0.0.1", 3248);
is = clientSocket.getInputStream();
} catch (IOException ex) {
// Do exception handling
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (is != null) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream("E:\\sss.java");
bos = new BufferedOutputStream(fos);
bytesRead = is.read(aByte, 0, aByte.length);
do {
baos.write(aByte);
bytesRead = is.read(aByte);
} while (bytesRead != -1);
bos.write(baos.toByteArray());
bos.flush();
bos.close();
clientSocket.close();
} catch (IOException ex) {
// Do exception handling
}
}
}
}
To accomplish what you're after you have to change quite a few things.
You can assume a specific protocol order, in the sense that the client needs to send a request to the server in order for the server to do anything, so the server is always in a listening state when the connection has been established.
You should,
Introduce a loop of sending requests and receiving responses
Figure out how to send a string object
Break up the file sending part so you don't allocate larger byte arrays than the OS can hand you (consider a file being 4GB for instance, allocating a byte array for the whole file can be troublesome)
So, with this in mind we can get going. Regarding step 1, this can be accomplished using a while loop. If we assume that the server always listens for a request, the server "request loop" can look something like this.
ClientRequest request;
while (request.getType() != RequestType.Complete) {
// receive new request
// depending on type, send response
}
We simply added two classes here, one ClientRequest that encapsulates a message from the client, and an enum RequestType that defines the type of request the client is interested in, for instance a file list or file contents.
public enum RequestType {
None, Complete, RequestFileList, RequestFileContent
}
public class ClientRequest {
private RequestType type;
public ClientRequest() {
type = RequestType.None;
}
public RequestType getType() {
return type;
}
}
Now we need to attach this to the socket somehow, so we add a method for receiving a request, and assign that request to the current request instance.
ClientRequest request = new ClientRequest();
while (request.getType() != RequestType.Complete) {
// receive new request
receiveRequest(clientSocket.getInputStream(), request);
if (request.getType() != RequestType.Complete) {
// pick a response
}
}
private void receiveRequest(DataInputStream socketStream, ClientRequest request) {
// get a type of request
byte type = socketStream.readByte();
request.setType(RequestType.from(type));
// get parameters for request, depending on type
if (request.getType() == RequestType.RequestFileContent) {
// receive file id (file name for instance, or some other id that you prefer)
String argument = readString(socketStream);
request.setArgument(argument);
}
}
This adds a from method in RequestType, to convert a byte to a request, a setType method in ClientRequest, and a readString method. We also add a new field and corresponding get and set methods in ClientRequest.
public enum RequestType {
// types as before
;
public static RequestType from(byte b) {
switch (b) {
case 1: return RequestType.Complete;
case 2: return RequestType.RequestFileList;
case 3: return RequestType.RequestFileContent;
default: return RequestType.None;
}
}
}
public class ClientRequest {
private String argument;
public void setType(RequestType value) {
type = value;
}
public String getArgument() {
return argument;
}
public void setArgument(String value) {
this.argument = value;
}
}
private String readString(DataInputStream socketStream) {
int length = socketStream.readInt();
byte[] stringBytes = new byte[length];
socketStream.read(stringBytes);
return new String(stringBytes, "UTF-8");
}
Now we get to the next step, responding to the request. Simply add a switch case and handle the type of request.
{
// depending on type, send response
handleRequest(clientSocket.getOutputStream(), request);
}
private void handleRequest(DataOutputStream socketStream, ClientRequest request) {
switch (request.getType()) {
case RequestType.RequestFileList: {
String[] fileList = getFileList(getCurrentDirectory());
// send response type
socketStream.write(ResponseType.ResponseFileList.getByte());
// send number of files
socketStream.writeInt(fileList.length);
// send each string
for (String fileName : fileList) {
sendString(socketStream, fileName);
}
}
break;
case RequestType.RequestFileContent: {
// send response type ResponseType.ResponseFileContent
// send length of file so other party can determine number of bytes to receive
// send file contents in chunks of a fixed byte array length
// send last part of file contents, if length of file is not evenly divided by array chunk size
}
break;
}
}
the sendString method is simply a "reversed order" of the readString method.
private void sendString(DataOutputStream socketStream, String value) {
int length = value.length();
socketStream.writeInt(length);
byte[] stringBytes = value.getBytes("UTF-8");
socketStream.write(stringBytes);
}
The ResponseType is an enum of values similar to the ones in RequestType, so the client can handle the type of response that the server sends.
With these changes, you will be able to request a file list and present the response of files that the server sends. When the user picks a file to receive, the client can send a new request to the server and the server can send the appropriate file contents to the client.
The client application will have to define a similar ClientRequest class (perhaps with the name ServerResponse) with corresponding methods that the server specified for reading from and writing to the socket streams. This can be abstracted further by encapsulating the socket in a class, with a listener pattern for when a request or response is received that the GUI can subscribe to, ..although this goes beyond my example.
If you feel I need to clarify anything, please leave a comment and I'll do my best to answer.
How do you ask files? By name! I think that server accepts commands and responds with responses. You may use the format for your commands to server: CMD_NAME, arg1, arg2, ... Arguments can be binary, dependent on command. By CMD_NAME, your server will distinguish what you want from it (either accept the file or provide one).
You have a problem that you accept only one type of request. You need a command mechanism to ask different requests from server. Server needs to parse those requests rather than give a hardcoded answer immediately. This will make it flexible.
http://www.javamex.com/tutorials/networking/simple_server_s_side.shtml I am sure that there a tons of other examples like this. Java sockets are reliable, there cannot be problem. Just start learning basics, how to communicate different messages between client and server. But, your problem is not Sockets-related at all. Imagine that you communicate over files: read requests from one and write responses into another. Which messages do you write then? This is called a protocol. You need to design a simple one.
Have you tried creating an array, so each file has it own index... When the client chooses the file he wants, then you return the file on certain array index.
~ btw you can serialize your array and send it to the client.
You can use ObjectOutputStream to send Strings or any other kind of Object via writeObject.
I referenced the code from Android Developer. After compliling the code and fooling around with some of the errors, I could not figure this one out.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import junit.framework.TestCase;
public class ConnectedThread extends Thread {
private static final String MESSAGE_READ = null;
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private Handler mHandler = new Handler();
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
}
catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
// buffer store for the stream
int bytes;
// bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main Activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
}
catch (IOException e) { }
}
/* Call this from the main Activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
The method obtainMessage(int, int , object) in the type Handler is not applicable for the arguments(String, int ,int, Byte[]).
Could someone that has used this code before possibly tell me what i need to ad or what i am missing. Its probably something really simple. Thank You.
obtainMessage accept argumets like
handler.obtainMessage(int)
handler.obtainMessage(int, object)
handler.obtainMessage(int, int, int)
handler.obtainMessage(int, int, int, object);
You have passes MESSAGE_READ variable in handler.obtainMessage() so it looks like
handler.obtainMessage(String, int, int, object);
MESSAGE_READ which is String and thats why you are getting this error
change MESSAGE_READ variable String to Int