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);
}
}
Related
I've learned in Java how to stream data over a network connection using ServerSocket and Socket, such as:
Client.java:
Socket socket = new Socket(address, port);
int i;
while ((i = System.in.read()) != -1)
socket.getOutputStream().write(i);
Server.java:
ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
int i;
while ((i = socket.getInputStream().read()) != -1)
System.out.println(i);
This would simply have Client blocking on System.in.read() at one end, and Server blocking on socket.getInputStream().read() at the other, and the bytes get passed when ENTER is pressed in the Client program.
How would I accomplish something similar within a single program, without using Sockets? For example, if I had Thread A waiting on keyboard input which is then streamed to Thread B which is able to "consume" the bytes at an arbitrary time in the future, just as Server (above) is able to consume bytes from socket.getInputStream() at some arbitrary time?
Is PipedInput/OutputStream the right solution for this, or ByteArrayInput/OutputStream, or something else? Or am I overthinking it?
Yes, you can use PipedInputStream/PipedOutputStream for "streaming" data "locally" in your JVM. You create one PipedInputStream and one PipedOutputStream instance, connect them with the connect() method and start sending/receiving bytes. Check the following example:
PipedInputStream pipedIn = new PipedInputStream();
PipedOutputStream pipedOut = new PipedOutputStream();
pipedIn.connect(pipedOut);
Thread keyboardReadingThread = new Thread() {
#Override
public void run() {
System.out.println("Enter some data:");
Scanner s = new Scanner(System.in);
String line = s.nextLine();
System.out.println("Entered line: "+line);
byte[] bytes = line.getBytes(StandardCharsets.UTF_8);
try {
pipedOut.write(bytes);
pipedOut.flush();
pipedOut.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Keyboard reading thread terminated");
}
};
keyboardReadingThread.start();
Thread streamReadingThread = new Thread() {
#Override
public void run() {
try {
int bytesRead = 0;
byte[] targetBytes = new byte[100];
System.out.println("Read data from the PipedInputStream instance");
while ((bytesRead = pipedIn.read(targetBytes)) != -1) {
System.out.println("read "+bytesRead+" bytes");
String s = new String(targetBytes, 0, bytesRead, StandardCharsets.UTF_8);
System.out.println("Received string: "+s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Streaming reading thread terminated");
}
};
streamReadingThread.start();
keyboardReadingThread.join();
streamReadingThread.join();
First the two piped stream instances are connected. After that two threads will read from the keyboard and read from the PipedInputStream instance. When you run your application you will get an output similar to this (with Some example input for testing being the keyboard input):
Enter some data:
Read data from the PipedInputStream instance
Some example input for testing
Entered line: Some example input for testing
Keyboard reading thread terminated
read 30 bytes
Received string: Some example input for testing
Streaming reading thread terminated
Also notice that the threads are not synchronized in any way, so the System.out.println() statements might get executed in a different order.
This is mostly an extension of the answer #VGR gave in the comments.
If the entirety of your "Network" exists within the same, single JVM, then you don't need anything like sockets at all - you can just use Objects and methods.
The entire point of Sockets was to allow the JVM to perform actions outside of itself (typically with another JVM somewhere in the outside world).
So unless you are trying to interact with objects outside of your current JVM, it is as simple as this.
public class ClientServerExample
{
public static void main(String[] args)
{
Server server = new Server();
Client client = new Client();
client.sendMessage("Hello Server", server);
}
static class Server
{
String respond(String input)
{
String output = "";
System.out.println("Server received the following message -- {" + input + "}");
//do something
return output;
}
}
static class Client
{
void sendMessage(String message, Server server)
{
System.out.println("Client is about to send the following message to the server -- {" + message + "}");
String response = server.respond(message);
System.out.println("Client received the following response from the server -- {" + response + "}");
//maybe do stuff with the response
}
}
}
Here is the result from running it.
Client is about to send the following message to the server -- {Hello Server}
Server received the following message -- {Hello Server}
Client received the following response from the server -- {}
Note that server doesn't return anything because I didn't do anything in the server. Replace that comment with some code of your own and you will see the results.
EDIT - to better explain a real world example, where a server will respond to requests in FIFO, here is a modified version of the above example.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ClientServerExample
{
public static void main(String[] args)
{
System.out.println("===========STARTING SYNCHRONOUS COMMUNICATION============");
synchronousCommunication();
System.out.println("===========FINISHED SYNCHRONOUS COMMUNICATION============");
System.out.println("===========STARTING ASYNCHRONOUS COMMUNICATION============");
asynchronousCommunication();
System.out.println("===========FINISHED ASYNCHRONOUS COMMUNICATION============");
}
public static void synchronousCommunication()
{
Server server = new Server();
Client client = new Client();
String response = "";
response = client.sendMessage("Good morning Server!", server).join();
System.out.println("Client received the following response from the server -- {" + response + "}");
response = client.sendMessage("Good evening Server!", server).join();
System.out.println("Client received the following response from the server -- {" + response + "}");
}
public static void asynchronousCommunication()
{
Server server = new Server();
Client client = new Client();
List<CompletableFuture<String>> responses = new ArrayList<>();
responses.add(client.sendMessage("Good morning Server!", server));
responses.add(client.sendMessage("Good evening Server!", server));
for (CompletableFuture<String> eachResponse : responses)
{
System.out.println("Client received the following response from the server -- {" + eachResponse.join() + "}");
}
}
static class Server
{
CompletableFuture<String> respond(final String input)
{
System.out.println("Server received the following message -- {" + input + "}");
return
CompletableFuture.supplyAsync(
() ->
{
try
{
//sleep for 2 seconds, to represent arbitrary delay in receiver processing
Thread.sleep(2000);
return input.contains("morning") ? "Good morning to you too!" : "Good evening to you too!";
}
catch (Exception e)
{
throw new IllegalStateException("What happened?", e);
}
});
}
}
static class Client
{
CompletableFuture<String> sendMessage(String message, Server server)
{
System.out.println("Client is about to send the following message to the server -- {" + message + "}");
return server.respond(message);
}
}
}
Both of these examples are performing a FIFO approach to data processing. They receive the request, calculate a response, and then send back a CompletableFuture, which is basically an Object that contains the response that will arrive once the Server gets around to it, sort of like a Promise in Javascript.
For the synchronous example, we see that a client message is sent, and then processed before the next one is sent. As a result, we have a minor delay between the 2 (about 2 seconds).
For the asynchronous example, we see that both client messages are sent, and their CompletableFutures are put into a batch list, which is converted to normal strings once all requests have been sent.
The synchronous example takes around 10 seconds.
The asynchronous example takes around 5 seconds.
Both of these are different ways of performing FIFO in the way that you described. They both are examples where multiple clients send a request to the server, and then the server finishes them when they get around to it. That 5 seconds delay is meant to represent the idea of "getting around to it". In reality, getting around to it usually means that the server has so much on it's plate that it will take a long time before it has a chance to give a full response.
Let me know if you need another example to better help you understand.
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 problem is if the server dies after the first successfully connection, i don't know how do i ask that the destiny is still alive? (See catch exception in the code). I execute the next step's:
1.- I start the client/server application
2.- I start my program (with a Thread)
3.- I send one message from the client to my program, my program delivers this message to the server, the server answers to my program, my program delivers the message back to the client successfully.
4.- Now, i kill and restart the client/server application (without restarting my program)
5.- I repeat step "3" but at this time, when the program reaches the "len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);" it produces the catch that i need to code for ask if the server is really alive (that is true in this step)". An attempt to read in this situation produces a "SocketException" with this description: "Software caused connection abort: recv failed java mail".
If i put in the code of catch that i need all instruction again for connect the socket and new streams, doesn't work too.
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;
public GenericInterceptorProcessConfigurations confs;
//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;
GenericInterceptorProcess(GenericInterceptorProcessConfigurations confs_p)
{
confs = confs_p;
prefix_log_messages = confs.prefix_log_messages;
}
#Override
public void run()
{
//OCCASIONAL USE
String aux;
try
{
logger("STARTING SERVER --- PORT NUMBER: " + confs.local_port);
//CREATING THE LOCAL SERVER SOCKET
serverSocketLocal = new ServerSocket(confs.local_port);
//THIS LOOP MAINTAINS THE CONNECTIVITY WITH ONE CLIENT AT TIME
while ( true )
{
//CONNECTION TO THE ORIGIN
logger("WAITING FOR A CONNECTION OF A CLIENT...");
socketForLocal = serverSocketLocal.accept();
streamFromOrigin = new DataInputStream(socketForLocal.getInputStream());
streamToOrigen = new DataOutputStream(socketForLocal.getOutputStream());
logger("CONNECTED CLIENT: " + socketForLocal.getRemoteSocketAddress() );
//CONNECTION TO THE DESTINY
try
{
socketToDestiny = new Socket();
socketToDestiny.setSoTimeout(confs.timeout_destiny);
socketToDestiny.connect(new InetSocketAddress(confs.destiny_ip,confs.destiny_port),confs.timeout_connections);
//CREATING THE DESTINY'S STREAMS
streamFromDestiny = new DataInputStream(socketToDestiny.getInputStream());
streamToDestiny = new DataOutputStream(socketToDestiny.getOutputStream());
}
catch(IOException ex)
{
logger("CONNECTION REJECTED BY DESTINY: " + ex.getMessage());
closeOriginStream();
continue;
}
logger("CONNECTED DESTINY: " + socketToDestiny.getRemoteSocketAddress() );
//THIS LOOP MAINTAINS THE MESSAGES'S CHANGES
while ( true )
{
logger("WAITING FOR A MESSAGE..");
//THIS TRY/CATCH EXITS FOR CONNECTION RESETS
try
{
len_message_from_origen = streamFromOrigin.read(buffer_msg_origin);
}
catch(SocketException ex)
{
closeAll();
break;
}
if ( len_message_from_origen < 0 )
{
closeAll();
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);
//MAKE THE CHANGES IN THE INPUT'S MESSAGE
ChangesInMessages.makeChanges(message_origin,confs.type_changes_for_input_messages);
aux = new String(message_origin);
logger("RECEIVED MESSAGE FROM ORIGIN WITH MODIFICATIONS: " + 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...");
//AT THIS POINY, WE MAY HAVE A PROBLEM IF THE SERVER DIES
len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);
}
catch (SocketTimeoutException ex)
{
logger("IT DIDN'T COULD RETRIEVE A MESSAGE FROM DESTINY (timeout): " + ex.getMessage());
continue;
}
catch (SocketException ex)
{
boolean flagDestinyStillDead = false;
//IF WE REACH THIS EXCEPTION, IT MINDS THE DESTINY HAS DIED AFTER THE FIRST
//SUSSECCESFULLY CONNECTION, THUS, WE HAVE TO ASK IF THE DESTINY IS REALLY ALIVE
//HOW DO I DO THAT?
//I DONT KNOW HOW TO DO THIS SECCTION///
//NOTE: IF THE SERVER STILL DEAD, I HAVE TO CANCEL THIS MESSAGE AND
//RESTART THE LOOP
if ( flagDestinyStillDead )
{
closeAll();
break;
}
}
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);
//MAKE THE CHANGES IN THE OUTPUT'S MESSAGE
ChangesInMessages.makeChanges(message_destiny,confs.type_changes_for_output_messages);
aux = new String(message_destiny);
logger("RECEIVED MESSAGE FROM DESTINY WITH MODIFICATIONS: " + 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.
I will attempt to answer your question using my definition of "alive" and "dead" that I put in the comment. So we know that if the server does not respond within 5 seconds, it is dead. Also, if we could not connect to the server within 5 seconds, then the server is also dead.
We can check if it is alive/dead like this:
boolean flagDestinyStillDead = false;
//give the server 5 seconds to do whatever it needs to get back alive
try {
Thread.sleep( 5000 );
}
catch ( InterruptedException ie ) {
//ignore this. this probably won't happen unless you purposely cause it
}
//we now create a new connection, because the old connection died
socketToDestiny = new Socket();
//we try connecting to the server
try {
socketToDestiny.connect(new InetSocketAddress(confs.destiny_ip,confs.destiny_port), 5000 );
//if our connection was successful, we also need to create a new input and output stream
streamToDestiny = new DataOutputStream( socketToDestiny.getOutputStream() );
streamFromDestiny = new DataInputStream( socketToDesinty.getInputStream() );
//we give the server 5 seconds to respond to any of our messages
socketToDestiny.setSoTimeout( 5000 );
//ask the server if its alive
streamToDestiny.writeUTF( "Are you alive?" );
//if the server responds, then by our definition of "alive", the server is alive
String receivedMessage = streamToDestiny.readUTF();
if ( receivedMessage.equals( "Yes, I am alive now!" ) ) {
flagDestinyStillDead = false;
}
//if the server did not respond, then we would get a SocketTimeoutException
//and we never would reach here
}
catch ( SocketTimeoutException e ) {
//server had 5 seconds to accept our connection, and since the connection timed out
//we presume that the server is still dead
flagDestinyStillDead = true;
}
catch ( IOException e ) {
//we gave the server 5 seconds already to get back alive using Thread.sleep(...)
//if any of our communications fail, then the server must be dead.
flagDestinyStillDead = true;
}
So, here's our process in English:
1. We lost connection to the server, oh no!
2. Ok, well if it doesn't respond in around 5 seconds, then we'll
presume it died
3. Fine, we'll wait 5 seconds.
4. Ok, 5 seconds passed. Let's connect again with 5 second timeout.
4.1 Connection is reestablished. Ok, now we send the server a message to check that it can respond.
4.1.1 We send the server a message and it responds. Ok, it's alive
4.1.2 We send the server a message and it doesn't respond after 5 seconds. Ok, it's dead
4.2 Connection is not reestablished. Ok, well we already waited 5 seconds. Since the server won't connect even after 5 seconds is up, we presume it's dead.
Please note that when I do streamToDestiny.writeUTF( "Are you alive?" ), you'll need some kind of readUTF() code on the server to read this message. Then the server has to writeUTF( "Yes, I am alive now!" ); back. You'll have to modify this small part of the code to fit with however your server and client runs.
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;
}
}
}
i'm working on a simple GUI chat program in Java. The goal is for the user to choose whether to host a server or to connect as a client. All of this works. The problem I'm having is letting either the client or the server chat. ideally, the user or the server can type into the textField and hit enter (or press the send button), and then the message will be sent to every client that is connected. During execution, the server runs an infinite while loop where it waits for more clients. The problem I'm having is two-fold:
1) I'm not sure if the way I'm passing the string to the inputstream is right, and 2) I don't know when I can have the server receive and then re-send the data, since it waits at server.accept().
here's the run method:
public void run()
{
conversationBox.appendText("Session Start.\n");
inputBox.requestFocus();
while (!kill)
{
if (isServer)
{
conversationBox.appendText("Server starting on port " + port + "\n");
conversationBox.appendText("Waiting for clients...\n");
startServer();
}
if (isClient)
{
conversationBox.appendText("Starting connection to host " + host + " on port " + port + "\n");
startClient();
}
}
}
here's the startClient method:
public void startClient()
{
try
{
Socket c = new Socket(host, port);
in = new Scanner(c.getInputStream());
out = new PrintWriter(c.getOutputStream());
while (true)
{
if (in.hasNext())
{
Chat.conversationBox.appendText("You Said: " + message);
out.println("Client Said: " + message);
out.flush();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
and here's the startServer method:
public void startServer()
{
try
{
server = new ServerSocket(port);
while (true)
{
s = server.accept();
conversationBox.appendText("Client connected from " + s.getLocalAddress().getHostName() + "\n");
}
}
catch (Exception e)
{
conversationBox.appendText("An error occurred.\n");
e.printStackTrace();
isServer = false;
reEnableAll();
}
}
And finally, here's the part of actionPerformed where I get the data and (attempt) to write it to the outputstream:
if (o == sendButton || o == inputBox)
{
if(inputBox.getText() != "")
{
out.println(inputBox.getText());
inputBox.setText("");
}
}
I guess my question is: How can I rearrange my methods so that the server can wait for text from the client and then send it back to all the clients? And, how do I send the text from the client to the server?
Among the problems with this code:
You keep creating clients and servers. Surely you should only do one of each?
You are performing blocking network operations on the event thread instead of in a separate thread.
You are looping at EOS via while (true) ... if in.hasNext(). This should be while (in.hasNext()) ...
You are accepting a socket and not apparently doing anything with it. It looks like you can only handle one client at a time. You should start a new thread to handle each accepted socket.
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.