So I have been working on a 2 player Tic-Tac-Toe game in java that utilizes sockets. All of the socket stuff is working, and I am sending data back and forth successfully between 2 clients and a server.
I have the following classes: Requester, Provider, and TBoard (which extends Serializable).
In the Requester (client) class, I instantiate an object of TBoard (TBoard board = new TBoard()).
I then send that object through the socket to my two clients, via an output stream.
The error I am getting is on the client-side, and it is: Exception in thread "main" java.lang.ClassCastException: java.lang.String
That's happening with:
board = (TBoard) in.readObject(); in:
do {
try {
board = (TBoard) in.readObject();
System.out.println(board.print_board());
} catch (ClassNotFoundException classNot) {
System.err.println("data received in unknown format");
}
My print_board() method in the TBoard class is meant to return a 2d array, but for right now (simplification purposes), I have the method returning the string "Hello"...
Does anyone know why this may be happening? I didn't want to bombard you all with code, but please let me know if posting any more may be helpful...
Thanks!
UPDATE:
Here is what I have going on (in more detail) with my Provider (server) class:
// 1. creating a server socket
providerSocket = new ServerSocket(20092);
// 2. Wait for connection
System.out.println("Waiting for connection...");
connection1 = providerSocket.accept();
System.out.println("Connection received from Player 1 " +
connection1.getInetAddress().getHostName());
connection2 = providerSocket.accept();
System.out.println("Connection received from Player 2 " + connection2.getInetAddress().getHostName());
// 3. get Input and Output streams
out = new ObjectOutputStream(connection1.getOutputStream());
out2 = new ObjectOutputStream(connection2.getOutputStream());
in = new ObjectInputStream(connection1.getInputStream());
out.writeObject("Player 1 has been connected successfully.");
in2 = new ObjectInputStream(connection2.getInputStream());
out2.writeObject("Player 2 has been connected successfully.");
out.flush();
out2.flush();
out.writeObject(board);
out2.writeObject(board);
So I am indeed sending a String in the streams before sending the last object (board). However, I am flushing out the streams beforehand. I also tried reset()'s after the flushes, and it still gave me the IllegalCastException...
IIRC, the class mentioned in the Exception is the one that was actually found, so in the code you're showing, the error would have to be here:
board = (TBoard) in.readObject();
And a String object instead of a TBoard being read from the stream.
Edit:
So you are sending strings in addition to the data. There's your problem. You either have to stop sending those strings, or read them on the receiving side before reading the data. Calling flush() is irrelevant to that - it just ensures that the Strings that you have already written to the stream are in fact sent over the connection and not kept in a buffer.
To investigate, I think the easiest would be to visualize (log or debug) the actual class that result from in.readObject(). Sample code:
Object o = in.readObject();
System.out.println("Object of class " + o.getClass().getName() + " is " + o);
There are a couple of possibilities. Assuming you are using ObjectOutputStream (which I think you are), it's possible that the error is arising because you are not fully serializing each object. ObjectOutputStream will try and hold of resending if you don't reset the stream each time.
I would try the following:
1) make sure you flush() and close() the sockets at the appropriate times
2) try calling reset() after each object is sent.
3) check that you are sending and receiving the same object type, just in case.
best of luck.
Related
This question already has answers here:
Sending the same but modifed object over ObjectOutputStream
(2 answers)
Closed 6 years ago.
So, I am trying to make simple java socket chat. Before any chat would be possible, simple login with only a username is required. I send "Message" ("Poruka" in code) objects over ObjectOutputStream, and "Message" objects contain "Sender, Receiver, Content, bool Login, bool Logout".
The sequence is:
Client sends Message (Poruka) with Login set to true and Sender set to username (works fine)
Server Thread successfuly receives the Message, and adds new user to list if similar username doesn't exist in list already (works fine)
Upon receiving information of adding user to the list on server side, Server Thread sends appropriate answer to the Client (here comes the issue).
The Server Thread code:
try {
Poruka poruka = null;
Poruka odgovor = new Poruka();
while (true) {
poruka = (Poruka) in.readObject();
System.out.println("salje prije ifLogin "+poruka.getSalje()+" "+ poruka.isLogin());
if (poruka.isLogin()) {
System.out.println("salje "+poruka.getSalje());
boolean success = Server.dodajKorisnika(poruka.getSalje());
System.out.println("Uspjeh? "+success);
//System.out.println("Jeste LOGIN poruka "+odgovor.getSadrzaj()+" "+odgovor.isLogout());
if (success) {
System.out.println("USLO U TRUE BLOK");
odgovor.setSadrzaj("ACCEPTED");
out.writeObject(odgovor);
// out.flush();
}
else{
odgovor.setSadrzaj("DENIED");
out.writeObject(odgovor);
// out.flush();
System.out.println(odgovor.getSadrzaj()+ " IZ BLOKA NEUSPJEH");
}
}
System.out.println("PORUKA " + poruka.isLogin() + " " + poruka.getSalje());
}
} catch (Exception ex) {
ex.printStackTrace();
}
Here, Server Thread does good job at setting return Message with appropriate login information. If login is successful, Message.Content is set to "ACCEPTED", else it's set to "DENIED". I double checked that.
Now, the problem: Client always receives Message object with "ACCEPTED" for some reason?
Here is the Client Thread code:
public boolean prijaviSe(String ime) {
boolean ret = false;
Poruka prijava = new Poruka();
prijava.setSalje(ime);
prijava.setLogin(true);
try {
System.out.println("PRIJAVA " + prijava.getSalje());
out.writeObject(prijava);
out.flush();
while (true) {
Poruka odgovor = (Poruka) in.readObject();
System.out.println("ODGOVOR "+odgovor.getSadrzaj());
if (odgovor.getSadrzaj().equals("ACCEPTED")) {
prijavljen = true;
System.out.println("accepted");
gui.setLabelaPrijavljen("Korisnik uspješno prijavljen");
break;
} else if (odgovor.getSadrzaj().equals("DENIED")) {
prijavljen = false;
System.out.println("denied");
gui.setLabelaPrijavljen("Promijenite korisničko ime!");
}
}//while
} catch (Exception ex) {
ex.printStackTrace();
}
return ret;
}
I am not sure even what to look for here? Is it a threading issue? Some kind of OutputStream / InputStream conflict? Or is it just my logic? Don't have a clue.
Java serialization is designed to serialize a graph of objects. If an object appears more than once it is only sent once and both references point to the same object. E.g. You can have two objects A and B where each has a reference to each other. But using references and only passing each object once, this all works
Where this doesn't work as expected is with mutable objects. If you pass an object more than once you get a reference to the same object. It does/can't check whether you changed it.
Note this means you are retaining every object ever written or read which can be a subtle memory leak.
The solution is to call reset() which clears the cached objects and sends any object again including updates.
I have a socketserver set up with a remote client, and it is functional. Upon opening the client and logging in, I noticed that sometimes, there is an error that seems to be due to the client reading an int when it shouldn't be.
Upon logging on, the server sends a series of messages/packets to the client, and these are anything from string messages to information used to load variables on the client's side.
Occasionally, while logging in, an error gets thrown showing that the client has read a packet of size 0 or a very large size. Upon converting the large-sized number into ascii I once found that it was a bit of a string "sk." (I located this string in my code so it's not entirely random).
Looking at my code, I'm not sure why this is happening. Is it possible that the client is reading an int at the wrong time? If so, how can I fix this?
InetAddress address = InetAddress.getByName(host);
connection = new Socket(address, port);
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
String process;
System.out.println("Connecting to server on "+ host + " port " + port +" at " + timestamp);
process = "Connection: "+host + ","+port+","+timestamp + ". Version: "+version;
write(0, process);
out.flush();
while (true) {
int len = in.readInt();
if (len < 2 || len > 2000) {
throw new Exception("Invalid Packet, length: "+len+".");
}
byte[] data = new byte[len];
in.readFully(data);
for (Byte b : data) {
System.out.printf("0x%02X ",b);
}
try {
reader.handlePackets(data);
} catch (Exception e) {
e.printStackTrace();
//connection.close();
//System.exit(0);
//System.out.println("Exiting");
}
}
//Here is code for my write function (Server sided):
public static void write(Client c, Packet pkt) {
for (Client client : clients) {
if (c.equals(client)) {
try {
out.writeInt(pkt.size());
out.write(pkt.getBytes());
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
So looking at the write function, I don't really see how it could be confusing the client and making it read for the size of the packet twice for one packet (at least that's what I think is happening).
If you need more information please ask me.
The client side code looks fine, and the server side code looks fine too.
The most likely issue is that this is some kind of issue with multi-threading and (improper) synchronization. For example, maybe two server-side threads are trying to write a packet to the same client at the same time.
It is also possible that your Packet class has inconsistent implementations of size() and getBytes() ... or that one thread is modifying a Packet objects while a second one is sending it.
I've got a client-server app I'm making and I'm having a bit of trouble when reading objects on the server.
After my server connects to a client socket, I build object input and output streams and pass them along to my service() method. In there, I'm supposed to handle different kinds of messages from the client. I can get a message from the client (that is, a Message object, of my design) just fine. But of course, what I want to do is have a loop so I can get a message, process it, and respond back.
So far, my code only works for a single message. When I added my loop, what happened was on every iteration, my server just kept reading the same message over and over again before my client got a chance to send a new message over the socket (I think this is what's happening, at least).
So what I really need to do is figure out how to make my service() method wait for new input. Any ideas? Or am I approaching this wrong? Do I need to create a new OIS on every iteration or...? Some code:
public void service(ObjectInputStream input, ObjectOutputStream output) throws IOException, Exception {
_shouldService = true;
while (_shouldService) {
// It just keeps reading the same message over and over
// I need it to wait here until the client sends a new message
// Unless I'm just approaching this all wrong!
NetworkMessage message = (NetworkMessage) input.readObject();
NetworkMessageHeader header = message.getHeader();
String headerType = header.getType();
if (headerType.equals(NetworkMessageHeader.NetworkMessageHeaderTypeConnect)) {
doLoginForMessage(message, output);
} else if (headerType.equals(NetworkMessageHeader.NetworkMessageHeaderTypeFiles)) {
doFilesList(message, output);
} else {
System.out.println("Unrecognized header type: " + headerType);
}
}
}
The ObjectOutputStream caches object representations and will not detect if you are resending the same instance over and over again from the client side, but with changes in it. If this is your scenario you need to call reset on the stream before each send.
NetworkMessage message = new NetworkMessage();
for(;;) {
message.setProperty(whatever);
oos.reset();
oos.writeObject(message);
}
I am feeling really stupid right now guys.... basically I am connecting over TCP on a local machine... and when I try to make the In/out streams at the client it wont get passed creating the object input stream. What gives? This stops after printing 2... no exceptions or anything... This isn't the first time I've used this class which is partialy why I am puzzled.
try {
System.out.println("1");
mySocket = new Socket("localhost", 11311);
System.out.println("12");
oos = new ObjectOutputStream(mySocket.getOutputStream());
System.out.println("2");
ois = new ObjectInputStream(mySocket.getInputStream());
System.out.println("13");
} catch (Exception e) {
e.printStackTrace();
}
From the specification of ObjectInputStream:
This constructor will block until the corresponding ObjectOutputStream
has written and flushed the header.
(For future readers:) I had the same problem because i made a silly change in server program and didn't test it for a long time then i was confused about why program is locked.
ServerSocket accepts the connection (responderSocket = serverSock.accept();) then suddenly for a inapropriate if (The silly change i mentioned!) program jumps out of the thread and because i didn't add a finally block to close streams and sockets the socket was left abandoned w/o sending or recieving anything (even stream headers). So in client side program there was no stream header (When i debbugged The code i saw that the last function executed before lock was:
public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
enableOverride = false;
readStreamHeader(); //// <== This function
bin.setBlockDataMode(true);
}
readStreamHeader();)
So be careful about what happens in server side, maybe problem isn't where you expecting it!
snippet from The Server code :
public void run() {
try {
// Create data input and output streams
ObjectInputStream inputFromClient = new ObjectInputStream(
socket.getInputStream());
ObjectOutputStream outputToClient = new ObjectOutputStream(
socket.getOutputStream());
while (true) {
cop = inputFromClient.readObject();
String[][] m1=new String[][] {{"1", "1","1"}};
Object xx=new getSerialModel(m1);
outputToClient.reset();
outputToClient.writeObject(xx);
outputToClient.flush();
}
}
snippet from the Client :
//////////////
/// sockt jop
try {
// Create a socket to connect to the server
socket = new Socket("127.0.0."+Math.round(50+Math.random()*50), 8000);
// Create an output stream to send data to the server
toServer = new ObjectOutputStream(socket.getOutputStream());
toServer.flush();
}
catch (IOException ex) {
msgArea.append('\n' + ex.toString() + '\n');
}
///////////////////
//***
///////////////////
buttonSave.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent ev)
{
System.out.println("Saving data is not implemented yet.");
String[][] m1={{"0","0","0"}};
for ( int i = 0 ; i < tableModel.getRowCount() ; i++ ){
{ for ( int j = 0 ; j < tableModel.getColumnCount() ; j++ )
m1[i][j]=(String)tableModel.getValueAt(i, j) ;
}
}
getSerialModel obt =new getSerialModel(m1);
try{
toServer.reset();
toServer.writeObject(obt);
toServer.flush();
}
catch (Exception ex) {
msgArea.append("cant reach the server its may be off" + '\n');
}
}
});
// button send msg
buttonsendtest.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent ev)
{
try{
fromServer = new ObjectInputStream(socket.getInputStream());
Object mdata = fromServer.readObject();
tableModel.setDataVector((((getSerialModel)mdata).getmodel()), columnNames);
table.updateUI();
}
catch (Exception ex) {
System.out.print(ex.getStackTrace());
msgArea.append("cant reach the server its may be off "+ ex.toString() + '\n');
}
}
});
When I try to read serializable object from the server multible times , I get this exception , for first time the reciever read it successfully .
java.io.StreamCorruptedException: invalid stream header: 00007571
how can I fix it ?
If you are creating multiple ObjectInputStream instances in series for the same socket input stream, this seems like a bad idea. If the server is writing multiple objects to the same output stream, then there is serialization-related information that only gets sent once per unique object, and only the first ObjectInputStream instance on the client would be able to reliably read this. Using only one ObjectInputStream instance per socket input stream and one ObjectOutputStream instance per socket output stream is probably the safest implementation.
Also, if you are writing multiple objects to the same ObjectOutputStream instance on the server side (i.e., multiple writeObject() calls), this can result in stream header problems due to potentially multiple references to the same objects (typically nested references) when they are read by the client's input stream
This problem occurs when the object output stream wraps a socket output stream since during normal serialization, the second and later references to an object do not describe the object but rather only use a reference. The client's ObjectInputStream does not reconstruct the objects properly for some reason due to a difference in the header information it is expecting (it doesn't retain it from previous readObject() calls); this only seems to happen with socket streams, not file I/O, etc. This problem does not occur with the first readObject() call but rather the second and subsequent ones.
If you want to continue to use the same socket stream to write multiple objects, you will need something like the following in the server code:
objectOut.reset()
objectOut.writeObject(foo);
The reset() call re-initializes the stream, ignoring the state of any objects previously sent along the stream. This ensures that each object is sent in its entirety without the handle-type references that are typically used to compress ObjectOutputStream data and avoid duplication. It's less efficient, but there should be no data corruption when read by the client.
From the documentation for ObjectInputStream.readObject(), I quote:
Read an object from the ObjectInputStream. The class of the
object, the signature of the class,
and the values of the non-transient
and non-static fields of the class and
all of its supertypes are read.
Default deserializing for a class can
be overriden using the writeObject and
readObject methods. Objects referenced
by this object are read transitively
so that a complete equivalent graph of
objects is reconstructed by
readObject.
The root object is completely restored when all of its fields and
the objects it references are
completely restored. At this point the
object validation callbacks are
executed in order based on their
registered priorities. The callbacks
are registered by objects (in the
readObject special methods) as they
are individually restored.
Exceptions are thrown for problems with the InputStream and for classes
that should not be deserialized. All
exceptions are fatal to the
InputStream and leave it in an
indeterminate state; it is up to the
caller to ignore or recover the stream
state.
Specified by:
readObject in interface ObjectInput
Returns:
the object read from the stream
Throws:
ClassNotFoundException - Class of a serialized object cannot be found.
InvalidClassException - Something is wrong with a class used by serialization.
StreamCorruptedException - Control information in the stream is inconsistent.
OptionalDataException - Primitive data was found in the stream instead of objects.
IOException - Any of the usual Input/Output related exceptions.
I'd guess that you're trying to read an object before one has been written to the object stream, or one where the output stream hasn't been flushed.
You are trying to read in an object of type 'Object'. Is that how it was serialized? You need to make sure that you are reading the object into the same class that it was written from, remember those pesky serialVersionUID warnings that come up? This is key to object serialization and reconstruction, hence the need for matching classes. Also the reason that you need to update your UID when your class structure changes.
Perhaps you're trying to read multiple times the same object from the stream, while the server wrote the object only once.
Or you're trying to use an ObjectInputStream before a corresponding ObjectOutputStream is created, and that invalidates the communication between the two. An ObjectOutputStream writes a serialization stream header upon its creation, and if it's not created before the corresponding ObjectOutputStream, that header is lost.