0

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.

Nico
  • 374
  • 2
  • 4
  • 17
  • Would it not be easier to use the traditional client/server model, and simply append to a log file when something interesting comes up? I'm not sure why you'd want something listening and repeating what you say, when you could easily write it down yourself. I don't think the middle man is necessary. – MeetTitan Jan 08 '15 at 00:17
  • Hello, yes i agree with you but i do not have access to the code of the client/server, instead i have access to the IP and Ports configurations. The program is for testing purposes. If we do this, next, we be able to change some parts of the messages. – Nico Jan 08 '15 at 01:45
  • Try commenting out your call to `socketToDestiny.setSoTimeout()` – MeetTitan Jan 08 '15 at 04:59
  • MMMM I will lose the "time out" feature and i need it. – Nico Jan 08 '15 at 12:10
  • As long as you know what you're doing. That line will crash your code after 15 seconds of it running. The javadoc says "With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout." I assume this to be your problem. – MeetTitan Jan 08 '15 at 16:49
  • @MeetTitan, We know now that the problem is with "isConnected" method. I am trying to fix that when it really requires reconnect to the server. – Nico Jan 08 '15 at 16:52

1 Answers1

0

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;
        }
    }
}
Community
  • 1
  • 1
user2570465
  • 2,437
  • 2
  • 18
  • 22
  • Hello @user2570465, thanks for the answer. But if the server never comes up to life again, the program will be always trying to connect. I changed the code in my original question. See the new catch, i think it is OK but when the program execute the "read" instruction in the catch code, it says the socket is closed. I don't understand why, i am creating a "new Socket()" :S – Nico Jan 08 '15 at 16:58
  • @Nico for the first question, "the program will be always trying to connect." Your program cannot know if the server will ever come back to life again. Thus, we will need to be more specific about reconnecting. You would have to decide, "Do I want to give it 5 more minutes and then assume the server will never come back to life?" or "Do I want to just keep trying on every iteration of the outer while loop?" That would be an implementation detail I think you need to pick – user2570465 Jan 08 '15 at 20:15
  • @Nico for the second question, "when the program execute the 'read' instruction in the catch code, it says the socket is closed." This happens for the following reason: Your server dies, so you close the socket, `socketToDestiny.close()`. You try to reconnect, but the server is still dead, so you throw IOException on `socketToDestiny.connect(...);`. Your socketToDestiny is still not connected to anything (in other words, it is closed). Then, your inner while loop finishes, you go back to the beginning and try to read() from the closed socket, which throws an exception – user2570465 Jan 08 '15 at 20:21
  • @Nico for the third question, "I don't understand why, I am creating a 'new Socket()'". I think of it like this: you make a connection (which is a Socket) with the server. The server dies, so your connection breaks. You need to make a **new** connection to the server if you want to keep communicating. I don't know off the top of my head if you need to do `socketToDestiny = new Socket()` before you do `socketToDestiny.connect(...)`. I do know for sure that you cannot communicate using the old connection that broke. – user2570465 Jan 08 '15 at 20:24
  • Thanks for yout comments, i will try fix the program. The problem is when i kill the server, and start again, in the next message, the "read" instruction appear never release the program :S. What changes on my code would yo do? I changed the catch with new instructions. – Nico Jan 08 '15 at 23:34
  • @Nico By "never release the program" do you mean it's stuck on that line and it doesn't keep going? If so, that's called "blocking." Essentially, when you call read(), the method waits until it receives the data its waiting for. You can deal with this either by multithreading your application or use `socketToDestiny.setSoTimeout()`. You probably didn't have this problem before you used my suggested changes. This is because you `setSoTimeout()` in the first socket, but by doing `socketToDestiny = new Socket()`, you created a completely new socket that by default does not have timeout. – user2570465 Jan 09 '15 at 00:24