Processing Net : avoid print disconnection message

Hello,
im writing a simple program using the Processing Net library, this program should be able to start/stop a TCP server on a given port.

I have noticed that calling voluntarily the stop() function i receive in the console output the following message :
Server SocketException: socket closed

test case :

import processing.net.*;

Server server;

void setup() 
{
    size(200, 200);
    
    try 
    {
        server = new Server(this, 3273); 
    }
    catch (Exception exception) {println("PORT ALREADY IN USE");};
}

void draw() 
{
background(color(0,0,0));
             
}

/******************************/
void keyTyped() 
{
    server.stop();
}

In my specific case i want to avoid this print because it is me that voluntatly want to close the server.

I tryed to extend the Server class as :

class HServer extends Server
{
    HServer(PApplet parent, int port) 
    {
        super(parent, port, null);
    }
    
    public void dispose() 
    {
        thread = null;

        if (clients != null) 
        {
          disconnectAll();
          clientCount = 0;
          clients = null;
        }

        try 
        {
          if (server != null) 
          {
            server.close();
            server = null;
          }
        } 
        catch (IOException e) 
        {
          //e.printStackTrace();
        }
    }

}

like you can see im just commenting out the e.printStackTrace(); and adding the constructor because there is not implicit constructor in Server class

But this solution doesnt work because the field Server.thread is not visible.

I could replace the entire Server class but it doesnt make sense to me.

Do you have some advice ?

Thanks in advance.

This package is built when building Processing. For a short immediate solution if this was an isolated library, one could rebuild it with the single line change aka. removing the print() statement. For this case, I think you would need to re-build Processing. This could be easier than just trying to figure out what java files you need to modify related to the processing.net package. Maybe it is not difficult to isolate them but I haven’t tried that before.

For a better and long term solution, you could submit a ticket in github. However, it might take some time before they provide a solution.

On a side note, the message shows up in the console. An easier approach is to ignore the messages all together. I mean, it is not interfering with your code or demonstrations, or is it?

Kf

1 Like

IMO, library devs do a big disservice to the Processing community (who are very tweaky by nature :upside_down_face:) by using the keyword private or leaving the default package access level. :sneezing_face:

As we can confirm from class Server’s source code: :robot:

Both fields thread & server were left as package-protected access level. And many other fields as well btW! :roll_eyes:

We could use reflection to force access to them all. But it’s too convoluted & a hassle! :sleeping:

A much clever workaround is to make a “.java” file for our “hacked” Server subclass, which is HServer. :sunglasses:

And then “lie” that the “HServer.java” file belongs to packageprocessing.net” too! :smiling_imp:

All of a sudden all those package-protected members become accessible to our custom HServer subclass! :money_mouth_face:

Here’s my take on that: :innocent:

“HServer.java”

package processing.net;

import java.io.IOException;
import processing.core.PApplet;

public class HServer extends Server {
  public HServer(PApplet parent, int port) {
    this(parent, port, null);
  }

  public HServer(PApplet parent, int port, String host) {
    super(parent, port, host);
  }

  @Override public void dispose() {
    thread = null;

    if (clients != null) {
      disconnectAll();
      clientCount = 0;
      clients = null;
    }

    try {
      if (server != null) {
        server.close();
        server = null;
      }
    }

    catch (final IOException e) {
      //e.printStackTrace();
    }
  }
}

However, the red message “Server SocketException: socket closed” still shows up! :exploding_head:

Your assumption that commenting out e.printStackTrace(); within method dispose() would get rid of it is wrong! :ghost:

According to the comment right there at the SocketException catch () {} block within method run(): :thinking:

Thrown when server.close(); is called and server is waiting on accept().

Method dispose() invokes server.close(); right here:

However, there’s nothing we can do from within Server::dispose(), b/c ServerSocket::close() method always throws a SocketException when ServerSocket::accept() is active: :weary:

  1. https://Docs.oracle.com/javase/10/docs/api/java/net/ServerSocket.html#close()
  2. https://Docs.oracle.com/javase/10/docs/api/java/net/ServerSocket.html#accept()
  3. https://Docs.oracle.com/javase/10/docs/api/java/net/SocketException.html

So the only workaround left is hacking Server::run() rather than Server::dispose(): :space_invader:

“HServer.java”

package processing.net;

import java.net.Socket;
import java.net.SocketException;
import java.io.IOException;

import processing.core.PApplet;

public class HServer extends Server {
  protected boolean stopRequested;

  public HServer(PApplet parent, int port) {
    this(parent, port, null);
  }

  public HServer(PApplet parent, int port, String host) {
    super(parent, port, host);
  }

  @Override public void dispose() {
    stopRequested = true;
    super.dispose();
  }

  @Override public void run() {
    while (Thread.currentThread() == thread) try {
      final Socket socket = server.accept();
      final Client client = new Client(parent, socket);

      //synchronized (clients) { // for Processing 3.3.6 and older!
      synchronized (clientsLock) { // for Processing 3.3.7 and newer!
        addClient(client);

        if (serverEventMethod != null) try {
          serverEventMethod.invoke(parent, this, client);
        }

        catch (final ReflectiveOperationException e) {
          System.err.println("Disabling serverEvent() for port " + port);
          serverEventMethod = null;

          final Throwable cause = e.getCause();
          (cause != null? cause : e).printStackTrace();
        }
      }
    }

    catch (final SocketException e) {
      // Thrown when server.close()'s called & server's waiting on accept():
      if (!stopRequested)
        System.err.println("Server SocketException: " + e.getMessage());
      thread = null;
    }

    catch (final IOException e) {
      e.printStackTrace();
      thread = null;
    }
  }
}

@kfrajer @GoToLoop first of all thanks to both of you for your time .

Ill try to explaine a little more my “weird” question and the solution i have found after your suggestions

CASE SCENARIO :

  • the thread is blocked waiting on accept()
  • an socket exception is thrown
    QUESTION :
  • Who has launched the exception ?
    ANSWER :
  • it’s me stopping the server —> OK, dont print nothing we know it
  • it is a real network problem —> ERROR, print the stack trace

As you can see my goal is to distinguish two separate cases and handle them of consequence .

As suggested i worked on the HServer.java file and extended the class.

As solution i added a boolean variable named “stopIsRequested” and overrided stop() and run() method with minimal difference from the original source code as follow :

package processing.net;

import processing.core.*;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;

public class HServer extends Server 
{
    boolean stopIsRequested ;
    
    
    public HServer(PApplet parent, int port) 
    {
        this(parent, port, null);
    }

    public HServer(PApplet parent, int port, String host) 
    {
        super(parent, port, host);
    }
 
 @Override public void stop() 
    {
    this.stopIsRequested = true ;
    dispose();
    }
    
@Override public void run() {
    
    this.stopIsRequested = false;
    
    while (Thread.currentThread() == thread) {
      try {
        Socket socket = server.accept();
        Client client = new Client(parent, socket);
        synchronized (clientsLock) {
          addClient(client);
          if (serverEventMethod != null) {
            try {
              serverEventMethod.invoke(parent, this, client);
            } catch (Exception e) {
              System.err.println("Disabling serverEvent() for port " + port);
              Throwable cause = e;
              // unwrap the exception if it came from the user code
              if (e instanceof InvocationTargetException && e.getCause() != null) {
                cause = e.getCause();
              }
              cause.printStackTrace();
              serverEventMethod = null;
            }
          }
        }
      } catch (SocketException e) {
        //thrown when server.close() is called and server is waiting on accept
        if( ! stopIsRequested ) { System.err.println("Server SocketException: " + e.getMessage()); };
        thread = null;
      } catch (IOException e) {
        //errorMessage("run", e);
        e.printStackTrace();
        thread = null;
      }
    }
    }
}

what you think about this ?

Thanks in advance.

1 Like

Apart from the messy lack of indentation, it looks gr8! :+1:

However, if we directly invoke Server::dispose() instead of Server::stop(), your stopIsRequested “trap” is skipped! :fearful:

So you should place it inside Server::dispose() instead; given Server::stop() is merely an alias of it. :wink:

BtW, no need to prefix stopIsRequested w/ keyword this in Java: :coffee:
Just stopIsRequested = true; works alright! :innocent:

P.S.: I’ve edited & included the stopRequested flag to my own version too! :crazy_face:

You are right !
unfortunatly in one of the costructor of the original server class there is :

96: parent.registerMethod(“dispose”, this);

and according to processing documentation :

public void dispose() {
    // Anything in here will be called automatically when 
    // the parent sketch shuts down. For instance, this might
    // shut down a thread used by this library.
  }

But then, if the sketch is closed by the user, rather than by an explicit call to Server::stop(), that red “Server SocketException: socket closed” message is displayed if the flag isn’t set within Server::dispose()! :weary:

I was under the impression you wanted to eliminate that message if there’s no actual error. :thinking:

The way your version is set up, normal sketch closing always results in that message error. :astonished:

sorry for my poor english, i mean you are correct, the true assignement should be done in dispose() , NOT in stop().

so the final form of our class should be :

package processing.net;

import processing.core.*;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;

public class HServer extends Server 
{
  boolean stopIsRequested ;

  public HServer(PApplet parent, int port) 
  {
    this(parent, port, null);
  }

  public HServer(PApplet parent, int port, String host) 
  {
    super(parent, port, host);
  }

  @Override public void run() {

    stopIsRequested = false;

    while (Thread.currentThread() == thread) {
      try {
        Socket socket = server.accept();
        Client client = new Client(parent, socket);
        synchronized (clientsLock) {
          addClient(client);
          if (serverEventMethod != null) {
            try {
              serverEventMethod.invoke(parent, this, client);
            } 
            catch (Exception e) {
              System.err.println("Disabling serverEvent() for port " + port);
              Throwable cause = e;
              // unwrap the exception if it came from the user code
              if (e instanceof InvocationTargetException && e.getCause() != null) {
                cause = e.getCause();
              }
              cause.printStackTrace();
              serverEventMethod = null;
            }
          }
        }
      } 
      catch (SocketException e) {
        //thrown when server.close() is called and server is waiting on accept
        if ( ! stopIsRequested ) { 
          System.err.println("Server SocketException: " + e.getMessage());
        };
        thread = null;
      } 
      catch (IOException e) {
        //errorMessage("run", e);
        e.printStackTrace();
        thread = null;
      }
    }
  }

  @Override public void dispose() {

    stopIsRequested = true;

    thread = null;

    if (clients != null) {
      disconnectAll();
      clientCount = 0;
      clients = null;
    }

    try {
      if (server != null) {
        server.close();
        server = null;
      }
    } 
    catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Thx very much for the support .
I hope this could help someone else in the same situation in the future .
Cya ! :wink:

2 Likes