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

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

package org.lastbamboo.common.ice;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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 javax.net.SocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

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 com.barchart.udt.net.NetServerSocketUDT;

/**
 * Factory for creating UDT sockets.
 */
public class BarchartUdtSocketFactory implements UdpSocketFactory {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private static final ExecutorService threadPool = 
        Executors.newCachedThreadPool(new ThreadFactory() {
            private volatile int count = 0;
            
            @Override
            public Thread newThread(final Runnable r) {
                final Thread t = new Thread(r, "UDT-Socket-Thread-"+count);
                t.setDaemon(true);
                count++;
                return t;
            }
        });

    private final SocketFactory sslSocketFactory;
    
    public BarchartUdtSocketFactory(final SocketFactory sslSocketFactory) {
        this.sslSocketFactory = sslSocketFactory;
    }

    @Override
    public void newEndpoint(final IoSession session, final boolean controlling,
            final OfferAnswerListener socketListener,
            final IceStunUdpPeer stunUdpPeer,
            final IceAgent iceAgent) {
        log.info("Creating new Barchart UDT Socket");
        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);
            }
        }

        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 socket on CONTROLLED agent.");
            final Runnable clientRunner = new Runnable() {
                @Override
                public void run() {
                    try {
                        // openClientSocket(session, socketListener);
                        openServerSocket(session, socketListener);
                    } catch (final Throwable t) {
                        log.error("Barchart socket exception", t);
                    }
                }
            };
            /*
            final Thread udtClientThread = new Thread(clientRunner,
                    "UDT-Controlled-Thread");
            udtClientThread.setDaemon(true);
            udtClientThread.start();
            */
            threadPool.execute(clientRunner);
        } else {
            // This actually happens second in the ICE process -- the
            // controlled agent is notified to start sending media first!
            log.debug("Creating UDT 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 socketRunner = new Runnable() {
                @Override
                public void run() {
                    try {
                        // openServerSocket(session, socketListener);
                        openClientSocket(session, socketListener);
                    } catch (final Throwable t) {
                        log.error("Barchart socket exception", t);
                    }
                }
            };
            /*
            final Thread serverThread = new Thread(socketRunner,
                    "UDT-Controlling-Thread");
            serverThread.setDaemon(true);
            serverThread.start();
            */
            threadPool.execute(socketRunner);
        }
    }

    protected void openClientSocket(final IoSession session,
        final OfferAnswerListener socketListener) throws 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 Socket clientSocket = new NetSocketUDTWrapper();
        
        log.info("Binding to address and port");
        clientSocket.bind(new InetSocketAddress(local.getAddress(),
            local.getPort()));

        log.info("About to connect...");
        clientSocket.connect(
            new InetSocketAddress(remote.getAddress(), remote.getPort()));
        log.info("Connected...notifying listener");

        if (sslSocketFactory instanceof SSLSocketFactory) {
            final SSLSocket sslSocket =
                (SSLSocket)((SSLSocketFactory)sslSocketFactory).createSocket(clientSocket, 
                    clientSocket.getInetAddress().getHostAddress(), 
                    clientSocket.getPort(), true);
            
            sslSocket.setUseClientMode(true);
            sslSocket.startHandshake();
            socketListener.onUdpSocket(sslSocket);
        } else {
            socketListener.onUdpSocket(clientSocket);
        }

        log.info("Exiting...");
    }

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

        log.info("Session local was: {}", local);
        log.info("Binding to port: {}", local.getPort());
        final ServerSocket ss = new NetServerSocketUDT();
        ss.bind(new InetSocketAddress(local.getAddress(), local.getPort()));
        final Socket sock = ss.accept();
        
        if (sslSocketFactory instanceof SSLSocketFactory) {
            final SSLSocket sslSocket =
                (SSLSocket)((SSLSocketFactory)this.sslSocketFactory).createSocket(sock,
                    sock.getInetAddress().getHostAddress(),
                    sock.getPort(), true);
            sslSocket.setUseClientMode(false);
            sslSocket.startHandshake();
            threadPool.execute(new RequestRunner(socketListener, sslSocket));
        } else {
            threadPool.execute(new RequestRunner(socketListener, sock));
        }
        
    }

    private static class RequestRunner implements Runnable {

        private final Logger localLog = LoggerFactory.getLogger(getClass());
        private final Socket sock;
        private final OfferAnswerListener socketListener;

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

        @Override
        public void run() {
            localLog.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: {}", 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();
            
            log.info("Open: "+dgChannel.isOpen());
            log.info("Connected: "+dgChannel.isConnected());
            log.info("Sleeping on channel to make sure it unbinds");
            Thread.sleep(400);
            log.info("Closed channel");
        } catch (final Exception e) {
            log.error("Error clearing session!!", e);
        } finally {
            stunUdpPeer.close();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy