Search code examples
javaraspberry-piserversocketboot

Bind Exception only when launched at boot [Raspbian]


I'm launching a java program during my Raspberry startup (which is turning on Raspbian). I used the /etc/rc.local file to do this and it works properly (I also tried /etc/init.d/ solution but I prefer the other one). I'm launching my raspbian directly on console mode, this way I can see my program's output.

My problem is : Manually starting my app, it works well. Starting automatically at boot, it raise a Bind Exception : Can't assign requested address.

Launching the program

On the /etc/rc.local file, I wrote this line. It launch a script which start my .jar program.

#! /bin/sh -e
#
# rc.local
#
# [...]

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
    printf "My IP address is %s\n" "$_IP"
fi
# \/ Added \/
/home/pi/Documents/DinnerTimePi/startApp

exit 0

My startApp script contains :

#! /bin/sh

cd /home/pi/Documents/DinnerTimePi/

date >> testStart.txt #Only for checking it runs at launch
java -jar DinnerTimeServer.jar

exit 0

Normal program output

Server initialised at : 192.168.1.35 address and : 35150 port.
_ #(Waiting for client to connect)

Java program part

My Java program is a ServerSocket doing some stuff, it works properly too.

public class TimeServer {
    //Instance of class : can have only one server at a time
    static private TimeServer instance;
    //Default values
    static private int port = 35150;
    static private String host = "192.168.1.35"; //Adapt for your network, give a fix IP by DHCP
    //Variables
    private ServerSocket server = null;


    protected TimeServer(String pHost, int pPort){ // <- I'm running this constructor with the default values above
        list = new LinkedList<ClientProcessor>();
        host = pHost; // 192.168.1.135
        port = pPort; // 35150
        try {
            // \/ Line 57 on my code (see stack trace) \/
            server = new ServerSocket(port, 10, InetAddress.getByName(host));
        } catch (BindException bind){
            bind.printStackTrace(); //Here is the problem !!
            System.exit(1);
        } catch (UnknownHostException hoste) {
            hoste.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } 
    }

As you can see, I used the IP address 192.168.1.35, which is static on my modem, and it works well. I supposed the port was used during the boot, so I tried different ports, as 6543 and 35150. But it does the same thing : working manually, but not working during auto startup.

Stack trace

This is what I see as output when booting my Raspberry.

[...]
[ OK ] Reached target Network is Online.
       Starting LSB: Start NTP deamon...
[ OK ] Started LSB: Start NTP deamon.
java.net.BindException: Can't assign requested address
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
    at java.net.ServerSocket.bind(ServerSocket.java:375)
    at java.net.ServerSocket.<init>(ServerSocket.java:237)
    at server.TimeServer.<init>(TimeServer.java:57)
    at server.TimeServer.getInstance(TimeServer.java:32)
    at server.Main_Server.main(Main_Server.java:12)
[ OK ] Started /etc/rc.local Compatibility.
       Starting Terminate Plymouth Boot Screen...
[...]

The thing I don't understand is why my program works well launching it by myself, and it doesn't when the system launch it. There is no root needs for my program.

I hope it is clear, don't hesitate if you need more informations.

Thanks for help ! :)

(Check out the entire program if needed on github, autolaunch branch)


Solution

  • Throw the lines of the constructor into a specific function:

    protected boolean initThis(String pHost, int pPort) {
        list = new LinkedList<ClientProcessor>();
        host = pHost; // 192.168.1.135
        port = pPort; // 35150
        try {
            // \/ Line 57 on my code (see stack trace) \/
            server = new ServerSocket(port, 10, InetAddress.getByName(host));
        } catch (BindException bind){
            bind.printStackTrace(); //Here is the problem !!
            return false;
        } catch (UnknownHostException hoste) {
            hoste.printStackTrace();
            return false; // Change this to true if you want it to stop here
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return false; // Change to true to stop here
        }
        return true;
    }
    

    Now in your constructor:

    protected TimeServer(String pHost, int pPort) {
        while(!initThis(pHost, pPort))
            Thread.sleep(500); // Wait 0.5 secs before retry
    }
    

    This will retry so many times until it works.