Search code examples
javasocketstcpclient

How do i ask the destiny is still alive?


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.


Solution

  • 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.