This question already has an answer here:
Java networking, really strange error
(1 answer)
Closed 8 years ago.
Im working on a little chat program and now i have a huge problem and i can not solve it.
I dont know where the mistake could be, for me the code is right. So i really really need help. I have 2 threads in my server, 1 thread for accepting clients and the other for the streams. And the thread for the streams is not working right. It sends only 1 time a message back to the client and multiple clients are also not working. And there is another strange problem. I can only send 1 message back, if i put the JOptionPane-message called "Sockets empty" in the else statement, without it doesnt work.
Here is the stream thread code:
private static Runnable streamThread = new Runnable()
{
public void run()
{
while(true)
{
if(!(socketList.isEmpty()))
{
for(int i = 0; i < socketList.size(); i++)
{
try
{
String key = socketList.get(i);
if(socketHashMap.containsKey(key))
{
Socket connection = socketHashMap.get(key);
ObjectInputStream ois = new ObjectInputStream(connection.getInputStream());
String response = (String) ois.readObject();
ObjectOutputStream oos = new ObjectOutputStream(connection.getOutputStream());
oos.writeObject(key + ": " + response);
oos.flush();
}
if(connection.isClosed())
{
JOptionPane.showMessageDialog(null, "Client closed connection", "Info", JOptionPane.INFORMATION_MESSAGE);
}
}
catch (IOException | ClassNotFoundException e) {
JOptionPane.showMessageDialog(null, "ERROR: " + e.getMessage(), "ALARM", JOptionPane.ERROR_MESSAGE);
try {
connection.close();
connection.shutdownInput();
connection.shutdownOutput();
listClientsModel.remove(i);
} catch (IOException e1) {
}
}
}
}
else
{
JOptionPane.showMessageDialog(null, "SOCKETS LEER", "Info", JOptionPane.INFORMATION_MESSAGE);
}
}
}
};
And if you want to see the complete servercode :
http://pastebin.com/sxGGRnJv
The documentation for getOutputStream says:
Closing the returned OutputStream will close the associated socket.
And that is the problem. You create an ObjectOutputStream, but don't keep a reference to that object around. As this object gets garbage collected, it will call close() which will close the socket's output stream.
Same problem with the InputStream.
You could, for example, create a class that contains your socket and input and output stream and store that in your map instead of just the socket. That way you can re-use the streams.
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 am implementing multiplayer game with server socket mechanism.
Game is running in one loop mechanism.
Server only broadcast any message to every player connected.
Server creates thread for every connected player
Messages are JSONs and they are being sent every 100ms from client side.
Messages are being broadcasted by server and read by client without any interval.
This is server loop:
while (true) {
try {
System.out.println("LISTENING...");
Socket clientSocket = serverSocket.accept();
System.out.println("GOT CONNECTION");
PlayerThread playerThread = new PlayerThread(clientSocket);
playerThread.start();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Exception occured on server");
break;
}
}
This is PlayerThread loop:
while (true) {
try {
String inputMessage = in.readUTF();
System.out.println("GOT: " + inputMessage);
JsonNode jsonNode = objectMapper.readTree(inputMessage);
String senderName = jsonNode.get("senderName").asText();
if (!players.contains(this)) {
playerName = senderName;
players.add(this);
}
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
And finally listening to messages:
public void listen() {
if (connected) {
try {
if (in.available() > 0) {
String inputMessage = in.readUTF();
System.out.println("GOT MESSAGE: " + inputMessage);
handleMessages(inputMessage);
}
} catch (IOException e) {
LOGGER.log(Level.INFO, "Connection lost");
connected = false;
}
} else
LOGGER.log(Level.INFO, "Player is not connected");
}
Method above is run in main game loop. It checks if there's something in inputStream and then reads it.
This is how correct message looks like:
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 52","posX":10.0,"posY":5.0}
It works ok when there are 2 players, but the more players connect the more probably is to get message like this (or similiar broken messages):
GOT MESSAGE: 0} U{"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 65","posX":10.0,"posY":5.0}
or
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 24","pos
There different errors in message like Y letter, half-message or multiple messages in one row. Why such thing happen? It looks like when there are more players and they write into output stream in server side, this stream is not read yet so they are appending and appending. But it doesn't explain why there are broken and most imporant, how to resolve it?
I can move reading stream to another thread because in.readUTF() locks process but I wanted to keep it synchronized in main game loop and I don't think this will help (am I wrong?)
You need to synchronize your write loop on an object that's common between all PlayerThreads so that messages don't interleave.
synchronized(/*Should be a "global" server object*/) {
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
}
My problem is when it tries to read the object the second time, it throws the exception:
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1356)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at Client.run(BaseStaInstance.java:313)
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1356)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at Client.run(BaseStaInstance.java:313)
The first time I send the exact same object message; however, when I try doing the same thing the second time, it throws the error above. Do I need to re-intialize the readObject() method? I even printed out the message object that is being received by the line below and its exact the same as the first instance where it works ok.
Object buf = myInput.readObject();
I'm assuming there's some problem with appending, but I really have no use for appending. I just want to read a fresh line everytime.
I'd really appreciate some help in fixing this bug. Thank you.
==================================
Before that one line, I'm just creating the input and output objects for the socket in the run() method. The object declaration is outside the run() method in the class:-
#Override
public void run() {
try {
sleep((int) 1 * 8000);
} catch (Exception e) {
e.printStackTrace();
}
try {
//Creating input and output streams to transfer messages to the server
myOutput = new ObjectOutputStream(skt.getOutputStream());
myInput = new ObjectInputStream(skt.getInputStream());
while (true) {
buf = myInput.readObject();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}
You're right; I don't close the object. I'm not sure how to do that.
The underlying problem is that you are using a new ObjectOutputStream to write to a stream that you have already used a prior ObjectOutputStream to write to. These streams have headers which are written and read by the respective constructors, so if you create another ObjectOutputStream you will write a new header, which starts with - guess what? - 0xAC, and the existing ObjectInputStream isn't expecting another header at this point so it barfs.
In the Java Forums thread cited by #trashgod, I should have left out the part about 'anew for each object at both ends': that's just wasteful. Use a single OOS and OIS for the life of the socket, and don't use any other streams on the socket.
If you want to forget what you've written, use ObjectOutputStream.reset().
And don't use any other streams or Readers or Writers on the same socket. The object stream APIs can handle all Java primitive datatypes and all Serializable classes.
I am trying to create a monitor of messages between two applications. The idea is this monitor works in the middle of simple client/server application, and log the messages to the standard output. This program must be against of fails of the client/server (disconnections, time out's, etc). In the code, i call the client as "origin" and the server as "destiny". The current problem is when the server dies my program doesn't know and when a new message from client comes, this error appears "Software caused connection abort: socket write error". When the server comes up to life again, this error continues persisting. I think when i ask in the code "if ( !socketToDestiny.isConnected() )", it is not really connected. I am sure the problem is how i manage the "close" at the stream too.
This is the code of the program, i hope you could help me.
package interceptorprocess;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
public class GenericInterceptorProcess implements Runnable
{
private final String prefix_log_messages = "[CONNECTOR]";
//COMMUNICATION'S ORIGIN'S VARIABLES
ServerSocket serverSocketLocal;
Socket socketForLocal;
DataInputStream streamFromOrigin;
DataOutputStream streamToOrigen;
int len_message_from_origen;
byte[] buffer_msg_origin = new byte[4096];
byte[] message_origin = null;
//COMMUNICATION'S DESTINY'S VARIABLES
Socket socketToDestiny;
DataInputStream streamFromDestiny;
DataOutputStream streamToDestiny;
int len_message_from_destiny;
byte[] buffer_msg_destiny = new byte[4096];
byte[] message_destiny;
#Override
public void run()
{
//OCCASIONAL USE
String aux;
try
{
logger("STARTING SERVER --- PORT NUMBER: " + "1234");
//CREATING THE LOCAL SERVER SOCKET
serverSocketLocal = new ServerSocket(1234);
//CREATING THE DESTINITY CONNECTION WITH 15 TIMEOUT'S SECONDS
socketToDestiny = new Socket();
socketToDestiny.setSoTimeout(15000);
//THIS LOOP MAINTAINS THE CONNECTIVITY WITH ONE CLIENT AT TIME
while ( true )
{
logger("WAITING FOR A CONNECTION OF A CLIENT...");
socketForLocal = serverSocketLocal.accept();
//CREATING THE ORIGIN'S STREAMS
streamFromOrigin = new DataInputStream(socketForLocal.getInputStream());
streamToOrigen = new DataOutputStream(socketForLocal.getOutputStream());
logger("CONNECTED CLIENT: " + socketForLocal.getRemoteSocketAddress() );
//THIS LOOP MAINTAINS THE MESSAGES'S CHANGES
while ( true )
{
logger("WAITING FOR A MESSAGE..");
len_message_from_origen = streamFromOrigin.read(buffer_msg_origin);
if ( len_message_from_origen < 0 )
{
closeOriginStream();
break;
}
message_origin = new byte[len_message_from_origen];
//SAVE THE ORIGIN'S MESSAGE INTO AN ARRAY WHO HAS THE EXACT SIZE OF THIS MESSAGE
System.arraycopy(buffer_msg_origin, 0, message_origin, 0, len_message_from_origen);
aux = new String(message_origin);
logger("RECEIVED MESSAGE FROM ORIGIN: " + aux);
//TRY TO CONNECT TO DESTINY
try
{
if ( !socketToDestiny.isConnected() )
socketToDestiny.connect(new InetSocketAddress("10.10.200.200",1234),5000);
}
catch(IOException ex)
{
logger("CONNECTION REJECTED BY DESTINY: " + ex.getMessage());
continue;
}
//CREATING THE DESTINY'S STREAMS
streamFromDestiny = new DataInputStream(socketToDestiny.getInputStream());
streamToDestiny = new DataOutputStream(socketToDestiny.getOutputStream());
logger("SENDING MESSAGE TO DESTINY: " + aux);
//I HAD TO PUT THIS BLOCK BECAUSE IF THE DESTINY APPLICATIONS FAILS
//OR NOT ANSWER, THE PROGRAM MUST KEEP LISTENING THE FOLLOWING MESSAGES
try
{
//SENDING MESSAGE TO DESTINY
streamToDestiny.write(message_origin);
//READING THE ANSWER MESSAGE
logger("READING MESSAGE FROM DESTINY...");
len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);
}
//IN ONE OF THE FOLLOWINGS TWO CATCHS I GET THE ERROR
catch (SocketTimeoutException ex)
{
logger("IT DIDN'T COULD RETRIEVE A MESSAGE FROM DESTINY: " + ex.getMessage());
continue;
}
catch (SocketException ex)
{
//THE "socketToDestiny.isConnected()" ALWAYS RETURNS TRUE SINCE THE FIRST SUCCESSFULLY
//CONNECTION, AFTER THAT, IF THE SOCKET IS DISCONNECTED, IT REMAINS RETURNING "true".
//THUS, I HAD TO MAKE THE NEXT CODE BLOCK
streamFromDestiny.close();
streamToDestiny.close();
socketToDestiny.close();
socketToDestiny = new Socket();
socketToDestiny.setSoTimeout(confs.timeout_destiny);
socketToDestiny.connect(new InetSocketAddress(confs.destiny_ip,confs.destiny_port),confs.timeout_connections);
streamFromDestiny = new DataInputStream(socketToDestiny.getInputStream());
streamToDestiny = new DataOutputStream(socketToDestiny.getOutputStream());
logger("TRYING TO RECONNECT WITH DESTINY AND SEND THE MESSAGE... ");
logger("READING MESSAGE FROM DESTINY AFTER ERROR...");
len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);
}
message_destiny = new byte[len_message_from_destiny];
//SAVE THE DESTINY'S MESSAGE INTO AN ARRAY WHO HAS THE EXACT SIZE OF THIS MESSAGE
System.arraycopy(buffer_msg_destiny, 0, message_destiny, 0, len_message_from_destiny);
aux = new String(message_destiny);
logger("RECEIVED MESSAGE FROM DESTINY " + aux);
//SENDING THE ANSWER BACK TO THE ORIGIN
logger("SENDING BACK THE MESSAGE TO ORIGIN...");
streamToOrigen.write(message_destiny);
logger("MESSAGE DELIVERED SUCCESSFULLY!");
} //INTERNAL LOOP OF MESSAGES
} //INTERNAL LOOP OF CLIENTS
} //TRY
catch(IOException ex )
{
logger("THE SERVICE DIED: " + ex.getMessage() );
ex.printStackTrace();
} //CATCH
} //RUN
private void closeDestinyStream() throws IOException
{
streamFromDestiny.close();
streamToDestiny.close();
}
private void closeOriginStream() throws IOException
{
streamFromOrigin.close();
streamToOrigen.close();
}
private void closeAll() throws IOException
{
closeDestinyStream();
closeOriginStream();
}
private void logger(String message)
{
System.out.println(Utilidades.date() + " " + prefix_log_messages + " " + message);
}
}
Regards!
Sorry for my english, i am not a native speaker.
According to Java API,
public boolean isConnected()
Returns the connection state of the socket.
Note: Closing a socket doesn't clear its connection state, which means this method will return true for a closed socket (see isClosed()) if it was successfuly connected prior to being closed.
Returns:
true if the socket was successfuly connected to a server
Since:
1.4
Note that even if you close the socket, isConnect() still returns true, so it's very likely that your isConnect() usage is not right.
According to Sumit Singh's answer to this question,
How do I check if a Socket is currently connected in Java?
socket.isConnected() returns always true once the client connects (and even after the disconnect) weird !!
So, even after disconnecting, isConnected() will return true. So my hypothesis (like yours) is that although you caught the SocketWrite exception, the isConnected() still returns true. I cannot test this out, because its not a working example. However, you can do something like this:
try {
//write message to server
} catch ( SocketException e ) {
//we lost the connection, right? then print if the socket is connected
System.out.println( socketToDestiny.isConnected() );
}
and see what the output is. If the output is still true, then we have discovered the problem. If my hypothesis is correct, I would propose that you try to reconnect in the catch statement. For example:
try {
//write message to server
} catch ( SocketException e ) {
//we lost the connection, so let's try to reconnect
while ( true ) {
try {
socketToDestiny.connect( ...ip address ... );
break;
} catch ( IOException e2 ) {
//keep trying to reconnect!
continue;
}
}
}
Good afternoon everybody!
I'm trying to create a simnple messagging application but I cannot find a solution to a big issue. Exactely there are two, big issues.
Here the code:
#Override
public void run() {
String incoming;
try {
while (true) {
if (!connected)
break;
// READING
if (reader.ready() && (incoming = reader.readLine()) != null) {
notifier.putCommand(incoming, this);
incoming = null;
}
// WRITING
synchronized (messagges) {
for (String message : messagges) {
System.out.println("SENDING MESSAGE TO CLIENT: " + message);
writer.println(message);
}
messagges.clear();
}
writer.println("b");
}
} catch (IOException e) {
MyLogger.log(e);
}
}
Problems:
If I don't every time write junk text to the client (writer.println("b")) I cannot read any message on the BufferedReader, sent from it. How is that possible?!
On client side I see only lots of "b" but anything sent inside the statement for (writer.println(message)). It's really strange, because when I print on the server SENDING MESSAGE TO CLIENT: [...] I see it in the console, but then the message is not sent.
What could it be the problem?
You need to read about how buffering works, and, in particular, learn to use the flush() method to instruct a buffered writer to actually send the data.