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.
Related
My title is probably not the most descriptive, but I'm going to try and show as much code as possible in hope that it'll help everyone understand my question better. Here's how my client side of my project queries the server for information. This is an example of a typical request:
private String GENERATEGROUPKEY()
{
/* `out` is a PrintWriter using the sockets output stream */
out.println("GENERATEGROUPKEY");
try
{
/* `in` is a BufferedReader using the sockets input stream */
String response = in.readLine();
String[] temp = response.split(" ");
return temp[1];
}
catch (IOException ex)
{
return null; // throw connection error to client
}
}
My issue is that at any time, the server can send an unsolicited message to the client through that same socket with information (think about it like a chat client receiving a message). My unsuccessful idea was to create a thread that listens for such a message as long as we're not in the middle of another query, but that was also unsuccessful because even though I'm interrupting that thread, it's still hogging the messages that should've gone to the client query.
private String GENERATEGROUPKEY()
{
out.println("GENERATEGROUPKEY");
listenThread.interrupt(); // block listenThread from recieving response
try
{
String response = in.readLine();
String[] temp = response.split(" ");
listenThread = new PulseThread(in); // we're done, so allow
listenThread.start();
return temp[1];
}
catch (IOException ex)
{
listenThread = new PulseThread(in); // we're done, so allow
listenThread.start();
return null; // throw connection error to client
}
}
Here's exactly what listenThread is
public class PulseThread extends Thread
{
private BufferedReader in;
public PulseThread(BufferedReader in)
{
this.in = in;
}
#Override
public void run()
{
while (true)
{
if (Thread.currentThread().isInterrupted())
{
break;
}
try
{
String line = in.readLine();
System.out.println(line);
String[] params = line.split(" ");
if (params[0].equals("PULSED"))
{
NotificationManager.sendNotification("You have been pulsed!", "Pulsed by: " + params[1]);
}
}
catch (Exception ex)
{
}
}
}
}
I was previously under the impression that interrupting the thread in the middle of the BufferedReader's blocking call with readLine() would just cancel the blocking call unless I'm doing something else wrong.
Any help would be greatly appreciated, thanks.
EDIT: So looking into my assumption just a few lines above this sentence, it seems interrupting the thread doesn’t cancel readLine(). I guess the interrupting thread idea is a no-go. What’s the right way to do this?
The general pattern here is that you want one thread processing output from the socket (blocking while waiting) and then dispatching messages out to the correct things that requested them.
One implementation I like and have used successfully in multiple projects is to add a randomly generated ID to “requests” as part of a generic header (including a message type as well) and have the server always mirror the ID back in the response, which allows the client to associate a request with a response without caring what type of message it is.
In concrete terms, something like a SocketMessenger class with 2 public functions: sendRequest(type, body, callback) and registerUnsolicitedHandler(type, callback).
sendRequest builds the message header with type and a randomly generated ID, adds it to a list of pending replies alongside a reference to the callback function, and then sends the completed message to the server.
registerUnsolicitedHandler does what it’s name suggests and adds the callback function to a map of message types to be used when an incoming message has no ID.
In the separate thread handling incoming messages, it deserialises incoming data to get the type and ID from the header, if the message has an ID it searches the pending reply list and calls the appropriate callback with the message body (probably scheduled on the main thread, I’m glossing over some detail like locking), else it searches the unsolicited handler list for the specified type and calls that callback.
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.
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.
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);
}
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.