⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 netaccesspoint.java

📁 stun的java实现
💻 JAVA
字号:
/*
 * Stun4j, the OpenSource Java Solution for NAT and Firewall Traversal.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package net.java.stun4j.stack;

import java.util.*;
import java.io.IOException;
import java.net.*;
import net.java.stun4j.*;
import java.util.logging.*;

/**
 * The Network Access Point is the most outward part of the stack. It is
 * constructed around a datagram socket and takes care about forwarding incoming
 * messages to the MessageProcessor as well as sending datagrams to the STUN server
 * specified by the original NetAccessPointDescriptor.
 *
 * <p>Organisation: Louis Pasteur University, Strasbourg, France</p>
 *                   <p>Network Research Team (http://www-r2.u-strasbg.fr)</p></p>
 * @author Emil Ivov
 * @version 0.1
 */

class NetAccessPoint
    implements Runnable
{
    private static final Logger logger =
        Logger.getLogger(NetAccessPoint.class.getName());
    NetAccessPoint()
    {
    }

    /**
     * Max datagram size.
     */
    private static final int MAX_DATAGRAM_SIZE = 8 * 1024;


    /**
     * The message queue is where incoming messages are added.
     */
    private MessageQueue messageQueue = null;

    /**
     * The socket object that used by this access point to access the network.
     */
    protected DatagramSocket sock;

    /**
     * Indicates whether the access point is using a socket that was created
     * by someone else. The variable is set to true when the AP's socket is
     * set using the <code>useExternalSocket()</code> method and is consulted
     * inside the stop method. When its value is true, the AP's socket is not
     * closed when <code>stop()</code>ing the AP.
     *
     * This variable is part of bug fix reported by Dave Stuart - SipQuest
     */
    private boolean isUsingExternalSocket = false;

    /**
     * A flag that is set to false to exit the message processor.
     */
    private boolean isRunning;

    /**
     * The descriptor used to create this access point.
     */
    private NetAccessPointDescriptor apDescriptor = null;

    /**
     * The instance to be notified if errors occur in the network listening
     * thread.
     */
    private ErrorHandler             errorHandler = null;

    /**
     * Used for locking socket operations
     */
    private Object socketLock = new Object();

    /**
     * Creates a network access point.
     * @param apDescriptor the address and port where to bind.
     * @param messageQueue the FIFO list where incoming messages should be queued
     * @param errorHandler the instance to notify when errors occur.
     */
    NetAccessPoint(NetAccessPointDescriptor apDescriptor,
                   MessageQueue             messageQueue,
                   ErrorHandler			    errorHandler)
    {
        this.apDescriptor = apDescriptor;
        this.messageQueue = messageQueue;
        this.errorHandler = errorHandler;
    }


    /**
     * Start the network listening thread.
     *
     * @throws IOException if we fail to setup the socket.
     */
    void start()
        throws IOException
    {
        synchronized(socketLock){

            //do not create the socket earlier as someone might want to set an
            // existing one was != null fixed (Ranga)
            if (sock == null)
            {
                this.sock = new DatagramSocket(getDescriptor().getAddress().
                                               getSocketAddress());
                this.isUsingExternalSocket = false;
                logger.info("Bound a socket on ap: " + toString());
            }

            sock.setReceiveBufferSize(MAX_DATAGRAM_SIZE);
            this.isRunning = true;
            Thread thread = new Thread(this);
            thread.start();
        }
    }

    /**
     * Returns the NetAccessPointDescriptor that contains the port and address
     * associated with this accesspoint.
     * @return the NetAccessPointDescriptor associated with this AP.
     */
    NetAccessPointDescriptor getDescriptor()
    {
        return apDescriptor;
    }

    /**
     * The listening thread's run method.
     */
    public void run()
    {
        while (this.isRunning)
        {
            try
            {
                int bufsize = sock.getReceiveBufferSize();
                byte message[] = new byte[bufsize];
                DatagramPacket packet = new DatagramPacket(message, bufsize);

                sock.receive(packet);

                RawMessage rawMessage = new RawMessage( message,
                    packet.getLength(), packet.getAddress(), packet.getPort(),
                    getDescriptor());

                messageQueue.add(rawMessage);
            }
            catch (SocketException ex)
            {
                if (isRunning)
                {
                    logger.log(Level.WARNING,
                               "A net access point has gone useless:", ex);

                    stop();
                    //Something wrong has happened
                    errorHandler.handleFatalError(
                        this,
                        "A socket exception was thrown while trying to "
                        + "receive a message.",
                        ex);
                }
                else
                {
                    //The exception was most probably caused by calling this.stop()
                    // ....
                }
            }
            catch (IOException ex)
            {
                logger.log(Level.WARNING,
                           "A net access point has gone useless:", ex);

                errorHandler.handleError(ex.getMessage(), ex);
                //do not stop the thread;
            }
            catch (Throwable ex)
            {
                logger.log(Level.WARNING,
                           "A net access point has gone useless:", ex);

                stop();
                errorHandler.handleFatalError(
                    this,
                    "Unknown error occurred while listening for messages!",
                    ex);
            }
        }
    }


    /**
     * Shut down the access point. Close the socket for recieving
     * incoming messages. The method won't close the socket if it was created
     * outside the stack (Bug Report - Dave Stuart - SipQuest). It also seems
     * that sometimes sockets don't immdeiately free their port and trying
     * to bind to that port immediately after gets us an exception. We therefore
     * null the socket, wait for 200 miliseconds and call the garbage collector
     * this seems to be working fine but is not what one could call neat
     * behaviour, so if you'd like to disable it - set the
     * net.java.stun4j.stack.HARD_SOCK_CLOSE property to anything different from
     * true. If you'd want us to wait more or less - set
     * net.java.stun4j.stack.WAIT_FOR_SOCK_CLOSE to the appropriate number of
     * miliseconds.
     *
     */
    synchronized void stop()
    {
        this.isRunning = false;

        //avoid a needless null pointer exception
        if (sock != null
            && isUsingExternalSocket == false)
        {
            synchronized (socketLock)
            {
                sock.close();

                logger.info("Closed socket on ap " + toString());
                sock = null;
                String hardSocketClose =
                    System.getProperty("net.java.stun4j.stack.HARD_SOCK_CLOSE");

                //sometimes sockts stay open even after closing them. make sure
                //this doesn't happen here. (unless the user doesn't want us to)
                if (hardSocketClose == null
                    || hardSocketClose.equalsIgnoreCase("true"))
                {

                    int waitForSockClose = 200;
                    try
                    {
                        String waitForSockCloseStr =
                            System.getProperty(
                                "net.java.stun4j.stack.WAIT_FOR_SOCK_CLOSE");
                        if (waitForSockCloseStr != null &&
                            waitForSockCloseStr.length() > 0){

                            waitForSockClose =
                                Integer.parseInt(System.getProperty(
                                    waitForSockCloseStr));
                        }
                    }
                    catch (Throwable t)
                    {
                        logger.log(
                            Level.WARNING,
                            "Failed to parse wait_for_sock_close prop", t);

                        if (waitForSockClose < 0)
                        {
                            waitForSockClose = 200;
                        }
                    }

                    //wait
                    try
                    {
                        wait(waitForSockClose);
                    }
                    catch (InterruptedException t)
                    {
                        logger.warning("Interrupted waiting for sock close.");
                    }
                    System.gc();
                }
            }
        }
    }

    /**
     * Sends message through this access point's socket.
     * @param message the bytes to send.
     * @param address message destination.
     * @throws IOException if an exception occurs while sending the message.
     */
    void sendMessage(byte[] message, StunAddress address)
        throws IOException
    {
        DatagramPacket datagramPacket = new DatagramPacket(
                                                message,
                                                0,
                                                message.length,
                                                address.getSocketAddress());
        synchronized(socketLock){
            sock.send(datagramPacket);
        }
    }

    /**
     * Returns a String representation of the object.
     * @return a String representation of the object.
     */
    public String toString()
    {
        return "net.java.stun4j.stack.AccessPoint@"
                +apDescriptor.getAddress()
                +" status: "
                + (isRunning? "not":"")
                +" running";
     }

     /**
      * Sets a socket for the access point to use. This socket will not be closed
      * when the AP is <code>stop()</code>ed (Bug Report - Dave Stuart - SipQuest).
      * @param socket the socket that the AP should use.
      */
     void useExternalSocket(DatagramSocket socket)
     {
         this.sock = socket;
         this.isUsingExternalSocket = true;
     }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -