Search code examples
javastunice4j

How to receive public IP and Port using Stun and ice4j


I'll try to be brief.

I wish to create communication between 2 java apps (that will later be transported to android) without passing through a server. As such, I have spent weeks looking around, and after a lot of work I found stun and ice4j. The best explanation of how to use ice4j I found here, and it pretty much showed me what I need to do to add stun servers to an agent (I don't really know what an agent is, just that it manages my communications with STUN and TURN), through this code:

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ice4j.Transport;
import org.ice4j.TransportAddress;
import org.ice4j.ice.Agent;
import org.ice4j.ice.IceMediaStream;
import org.ice4j.ice.harvest.StunCandidateHarvester;

public class ice4jTesting {

    public static void main(String[] args) {

        Agent agent = new Agent();
        String[] hostnames = new String[] {"jitsi.org", "numb.viagenie.ca", "stun.ekiga.net"};

        for(String hostname: hostnames) {
            try {
                TransportAddress address;

                address = new TransportAddress(InetAddress.getByName(hostname), 3478, Transport.UDP);
                agent.addCandidateHarvester(new StunCandidateHarvester(address));
            } catch (UnknownHostException ex) {
                Logger.getLogger(SimpleStun.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        IceMediaStream stream = agent.createMediaStream("audio");
        int port = 5000;
        try {
            agent.createComponent(stream, Transport.UDP, port, port, port+100);
            // The three last arguments are: preferredPort, minPort, maxPort
        } catch (IllegalArgumentException | IOException ex) {
            Logger.getLogger(SimpleStun.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

however, after this the tutorial utilizes SDPUtils, a class that is in the source code of ice4j I found on github, to recieve the SDP information from the agent. However I got ice4j.jar from the central maven repository, and added it to my netbeans regular project (I did this because I am not very familiar with maven, and just wanted a regular library on my regular project). This jar library does not have the SDPUtils class, and since I don't really understand enough of this code to fix it myself, I was wondering if any of you could help me either fix the code above, or show me an example of how to answer the question on the title.

However, unless you can either do what I said in the last sentence, or point me to some sample code, your help will most likely not be useful, since I am mentally incapable of understanding the theory behind this completely because of the many concepts I do not know.

I have until the end of this week to figure this out, and if I don't I'm pretty screwed. So please, if you can or know someone that can help, I would extremely appreciate it.

Thanks for reading it so far and trying to help :)


Solution

  • There you go
    SdpUtils.java

    Actually i'm also working on the same as my University project. From last week i'm digging web for p2p connection establish over nat.
    I know that form where you got above Code snipet, i would like to inform you that there is errors in that code Here is the one that i corrected

    import java.io.IOException;
    import java.net.BindException;
    import java.net.InetAddress;
    import org.ice4j.Transport;
    import org.ice4j.TransportAddress;
    import org.ice4j.ice.Agent;
    import org.ice4j.ice.IceMediaStream;
    import org.ice4j.ice.harvest.StunCandidateHarvester;
    
    public class IceTest {
    
    public static void main(String[] args) throws Exception {
        Agent agent = new Agent(); // A simple ICE Agent
    
        /*** Setup the STUN servers: ***/
        String[] hostnames = new String[] { "jitsi.org", "numb.viagenie.ca", "stun.ekiga.net" };
        // Look online for actively working public STUN Servers. You can find
        // free servers.
        // Now add these URLS as Stun Servers with standard 3478 port for STUN
        // servrs.
        for (String hostname : hostnames) {
            try {
                // InetAddress qualifies a url to an IP Address, if you have an
                // error here, make sure the url is reachable and correct
                TransportAddress ta = new TransportAddress(InetAddress.getByName(hostname), 3478, Transport.UDP);
                // Currently Ice4J only supports UDP and will throw an Error
                // otherwise
                agent.addCandidateHarvester(new StunCandidateHarvester(ta));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /*
         * Now you have your Agent setup. The agent will now be able to know its
         * IP Address and Port once you attempt to connect. You do need to setup
         * Streams on the Agent to open a flow of information on a specific
         * port.
         */
    
        IceMediaStream stream = agent.createMediaStream("audio");
        int port = 5000; // Choose any port
        try {
            agent.createComponent(stream, Transport.UDP, port, port, port + 100);
            // The three last arguments are: preferredPort, minPort, maxPort
        } catch (BindException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
        /*
         * Now we have our port and we have our stream to allow for information
         * to flow. The issue is that once we have all the information we need
         * each computer to get the remote computer's information. Of course how
         * do you get that information if you can't connect? There might be a
         * few ways, but the easiest with just ICE4J is to POST the information
         * to your public sever and retrieve the information. I even use a
         * simple PHP server I wrote to store and spit out information.
         */
        String toSend = null;
        try {
            toSend = SdpUtils.createSDPDescription(agent);
            // Each computersends this information
            // This information describes all the possible IP addresses and
            // ports
        } catch (Throwable e) {
            e.printStackTrace();
        }
    
        /*The String "toSend" should be sent to a server. You need to write a PHP, Java or any server. 
         * It should be able to have this String posted to a database. 
         * Each program checks to see if another program is requesting a call. 
         * If it is, they can both post this "toSend" information and then read eachother's "toSend" SDP string. 
         * After you get this information about the remote computer do the following for ice4j to build the connection:*/
    
        String remoteReceived = ""; // This information was grabbed from the server, and shouldn't be empty.
        SdpUtils.parseSDP(agent, remoteReceived); // This will add the remote information to the agent.
        //Hopefully now your Agent is totally setup. Now we need to start the connections:
    
        agent.addStateChangeListener(new StateListener()); // We will define this class soon
        // You need to listen for state change so that once connected you can then use the socket.
        agent.startConnectivityEstablishment(); // This will do all the work for you to connect
    }
    

    }

    This code Requires SIP server to be setup and the one on ice4j test is saying something else just have a look at Ice.java