I'm working on a Client-Server project (you can call it "Broadcast Chat") but I got a problem with ObjectInputStream. It always retrieves me a an empty List, I found a solution but I don't know why it works...
This is the bugged code (check the server's sendMsg() function):
SERVER:
public class CoreServer implements Runnable {
private Socket sock;
private ServerConnect sc;
private ObjectOutputStream oos;
private ObjectInputStream ois;
private boolean running=true;
private List<String> lstr;
public CoreServer(Socket sock, ServerConnect sc) {
this.sock=sock;
this.sc=sc;
}
@Override
public void run() {
lstr=new LinkedList<String>();
try {
oos= new ObjectOutputStream(sock.getOutputStream());
ois=new ObjectInputStream(sock.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
while (running){
Object o=null;
try {
o= ois.readObject();
} catch (ClassNotFoundException | IOException e) {
// TODO Auto-generated catch block
sc.remove(this);
stop();
}
if (Integer.class.isInstance(o)){
try {
int num= (Integer) o;
if(num==0){
sendMsg();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if (String.class.isInstance(o)){
System.out.println("String Received");
String ss= (String) o;
sc.readyToSend(ss);
}
}
}
public void sendMsg() throws IOException{
try {
System.out.println("I'm going to send: "+lstr);
oos.writeObject((Object)lstr);
oos.flush();
lstr.clear();
// If I replace lstr.clear() with "lstr=new LinkedList();" it works as it should.
} catch (IOException e) {
e.printStackTrace();
}
}
public void addMsg(String text){
System.out.println("I will add -"+text+"- to the list");
lstr.add(text);
}
}
CLIENT:
public class ClientConnect implements Runnable {
private Socket sock;
private boolean running=true;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean first=true;
private Object o;
private ClientFrame cf;
public ClientConnect(Socket sock, ClientFrame cf){
this.sock=sock;
this.cf=cf;
}
@Override
public void run() {
if (first){
try {
oos= new ObjectOutputStream(sock.getOutputStream());
ois= new ObjectInputStream(sock.getInputStream());
first=false;
} catch (IOException e) {
e.printStackTrace();
}
}
while (running){
try {
oos.writeObject(new Integer(0));
oos.flush();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Connection error");
this.stop();
System.exit(1);
}
try {
o=ois.readObject();
System.out.println("I received o : "+(List)o);
} catch (ClassNotFoundException | IOException e) {
JOptionPane.showMessageDialog(null, "Server offline");
System.exit(1);
}
if(List.class.isInstance(o)){
List<String> l=null;
l=(List<String>) o;
Iterator<String> it= l.iterator();
while (it.hasNext()){
String s=it.next();
System.out.println("Adding:"+s);
cf.history.append(s+ "\n"); //this function will show the received content on a JTextArea
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void send(String text){
if (!text.equals("")){
try {
oos.writeObject(text);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The client is supposed to send something that I wrote to the server that will then send the same message to every client connected. This is what happens server-side and client-side when I write something:
Server Log:
Waiting client
Found one client
Waiting client //The Server is multithreaded and is waiting for another client to connect
Waiting client
I'm going to send: []
I'm going to send: []
I'm going to send: []
String Received
I will add -Hello- to the list
I'm going to send: [Hello]
I'm going to send: []
I'm going to send: []
I'm going to send: []
String Received
I will add -Stackoverflow- to the list
I'm going to send: [Stackoverflow]
I'm going to send: []
I'm going to send: []
I'm going to send: []
I'm going to send: []
Client Log:
I received o : []
I received o : []
I received o : []
You wrote: Hello
I received o : [] <-- it can't read the list that the server sent to me.
I received o : []
I received o : []
I received o : []
You wrote: Stackoverflow
I received o : [] <-- it can't read the list that the server sent to me.
I received o : []
I received o : []
I received o : []
I received o : []
If I replace lstr.clear() in sendMsg() with "lstr=new LinkedList();" it works as it should but I don't know why :(
Client Log (after fix):
I received o : []
I received o : []
I received o : []
You wrote: Hello
I received o : [Hello]
Adding:Hello
I received o : []
I received o : []
I received o : []
You wrote: Stackoverflow
I received o : [StackOverflow]
Adding:StackOverflow
I received o : []
I received o : []
I received o : []
I received o : []
How to explain this behavior?
The serialization protocol preserves object identity.
If you repeatedly write the same object, it will not be serialized with its contents again, the stream will just include a backreference to indicate that you wrote the same object another time.
As a result, the stream will only contain the state of your list as you first sent it, i.e. empty.
When you later add stuff to the list and write the list again, that won't get picked up. The stream will just contain a label saying "that list from before again".
When you change from list.clear()
to making a whole new list instance, you fix this: Now, every list is written to the stream with its data at the time of writing.