All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lastbamboo.common.ice.UdtSocketFactory Maven / Gradle / Ivy

The newest version!
package org.lastbamboo.common.ice;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.DatagramChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import org.lastbamboo.common.offer.answer.OfferAnswerListener;
import org.lastbamboo.common.stun.server.StunServer;
import org.littleshoot.mina.common.IoAcceptor;
import org.littleshoot.mina.common.IoService;
import org.littleshoot.mina.common.IoSession;
import org.littleshoot.mina.transport.socket.nio.support.DatagramSessionImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import udt.UDTClient;
import udt.UDTReceiver;
import udt.UDTServerSocket;
import udt.UDTSocket;

/**
 * Factory for creating UDT sockets.
 */
public class UdtSocketFactory implements UdpSocketFactory {
    
    private final Logger log = LoggerFactory.getLogger(getClass());
    
    private final ExecutorService m_threadPool = 
        Executors.newCachedThreadPool(new ThreadFactory() {
            private int threadNumber = 0;
            public Thread newThread(final Runnable r) {
                final Thread t = 
                    new Thread(r, "UDT-Socket-Accept-Thread-"+threadNumber);
                t.setDaemon(true);
                threadNumber++;
                return t;
            }
        });
    
    @Override
    public void newEndpoint(final IoSession session, final boolean controlling,
        final OfferAnswerListener socketListener, 
        final IceStunUdpPeer stunUdpPeer, final IceAgent iceAgent) {
        if (session == null) {
            log.error("Null session: {}", session);
            return;
        }

        // Wait for a bit before we clear the decoders and such on that port -
        // basically the client side may have sent a USE-CANDIDATE binding
        // request for a pair that's still in the in progress state on the
        // other end -- i.e. the server side hasn't verified the pair works
        // for it. So the server side could still be doing STUN checks at that
        // point, and we need to wait.
        //
        // We only do this on the controlling side due to an implementation
        // detail of how we're using this -- basically using HTTP the client
        // side always sends data before the server side (request -> response),
        // so there's no chance the server side could start sending media data
        // while we're still looking for STUN messages (the potential problem
        // on the server side that this sleep solves).
        if (controlling) {
            final long sleepTime = 1200;
            log.info("Client side sleeping for {} milliseconds", sleepTime);
            try {
                Thread.sleep(sleepTime);
            } catch (final InterruptedException e) {
                log.warn("Sleep interrupted?", e);
            }
        }
        
        UDTReceiver.connectionExpiryDisabled = true;
        clear(session, stunUdpPeer, iceAgent);
        if (!controlling) {
            // The CONTROLLED agent is notified to start the media stream first
            // in the ICE process, so this is called before the other side
            // starts sending media. We have to consider this in terms of
            // making sure we wait until the other side is ready.
            log.debug("Creating UDT client socket on CONTROLLED agent.");
            final Runnable clientRunner = new Runnable() {
                public void run() {
                    try {
                        // openClientSocket(session, socketListener);
                        openServerSocket(session, socketListener);
                    } catch (final Throwable t) {
                        log.error("Client socket exception", t);
                    }
                }
            };

            final Thread udtClientThread = new Thread(clientRunner,
                    "UDT Client Thread");
            udtClientThread.setDaemon(true);
            udtClientThread.start();
        } else {
            // This actually happens second in the ICE process -- the
            // controlled agent is notified to start sending media first!
            log.debug("Creating UDT server socket on CONTROLLING agent.");
            log.debug("Listening on: {}", session);

            // If we call "accept" right away here, we'll kill the
            // IoSession thread and won't receive messages, so we
            // need to start a new thread.
            final Runnable serverRunner = new Runnable() {
                public void run() {
                    try {
                        // openServerSocket(session, socketListener);
                        openClientSocket(session, socketListener);
                    } catch (final Throwable t) {
                        log.error("Server socket exception", t);
                    }
                }
            };
            final Thread serverThread = new Thread(serverRunner,
                    "UDT Accepting Thread");
            serverThread.setDaemon(true);
            serverThread.start();
        }
    }

    protected void openClientSocket(final IoSession session,
            final OfferAnswerListener socketListener)
            throws InterruptedException, IOException {
        final InetSocketAddress local = (InetSocketAddress) session
                .getLocalAddress();
        final InetSocketAddress remote = (InetSocketAddress) session
                .getRemoteAddress();

        log.info("Session local was: {}", local);
        log.info("Binding to port: {}", local.getPort());

        final UDTClient client = new UDTClient(local.getAddress(),
                local.getPort());


        log.info("About to connect...");
        client.connect(remote.getAddress(), remote.getPort());
        log.info("Connected!!!");

        final Socket sock = client.getSocket();
        log.info("Got socket...notifying listener");

        socketListener.onUdpSocket(sock);
        log.info("Exiting...");
    }

    protected void openServerSocket(final IoSession session,
            final OfferAnswerListener socketListener)
            throws InterruptedException, IOException {
        final InetSocketAddress local = (InetSocketAddress) session
                .getLocalAddress();

        log.info("Session local was: {}", local);
        log.info("Binding to port: {}", local.getPort());
        final UDTServerSocket server = new UDTServerSocket(local.getAddress(),
                local.getPort());

        final UDTSocket sock = server.accept();
        m_threadPool.execute(new RequestRunner(socketListener, sock));
    }

    public static class RequestRunner implements Runnable {

        private final Logger log = LoggerFactory.getLogger(getClass());
        private final UDTSocket sock;
        private final OfferAnswerListener socketListener;

        public RequestRunner(final OfferAnswerListener socketListener,
                final UDTSocket sock) {
            this.socketListener = socketListener;
            this.sock = sock;
        }

        public void run() {
            log.info("NOTIFYING SOCKET LISTENER!!");
            socketListener.onUdpSocket(sock);
        }
    }

    private void clear(final IoSession session, final IceStunUdpPeer stunUdpPeer, 
        final IceAgent iceAgent) {
        log.info("Closing ICE agent");
        iceAgent.close();
        log.info("Clearing session!!");
        final DatagramSessionImpl dgSession = (DatagramSessionImpl) session;
        final DatagramChannel dgChannel = dgSession.getChannel();
        session.close().join(10 * 1000);

        final StunServer stunServer = stunUdpPeer.getStunServer();
        stunServer.close();
        try {
            final IoService service = session.getService();
            log.info("Service is: {}", service);
            if (IoAcceptor.class.isAssignableFrom(service.getClass())) {
                log.info("Unbinding all!!");
                final IoAcceptor acceptor = (IoAcceptor) service;
                acceptor.unbindAll();
            }
            session.getService().getFilterChain().clear();
            dgChannel.disconnect();
            dgChannel.close();
        } catch (final Exception e) {
            log.error("Error clearing session!!", e);
        } finally {
            stunUdpPeer.close();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy