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

uk.co.real_logic.aeron.driver.media.UdpChannelTransport Maven / Gradle / Ivy

/*
 * Copyright 2014 - 2015 Real Logic Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package uk.co.real_logic.aeron.driver.media;

import uk.co.real_logic.aeron.driver.Configuration;
import uk.co.real_logic.aeron.driver.LossGenerator;
import uk.co.real_logic.aeron.driver.event.EventCode;
import uk.co.real_logic.aeron.driver.event.EventLogger;
import uk.co.real_logic.aeron.protocol.HeaderFlyweight;
import uk.co.real_logic.agrona.LangUtil;
import uk.co.real_logic.agrona.concurrent.UnsafeBuffer;

import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;

import static uk.co.real_logic.aeron.logbuffer.FrameDescriptor.frameLength;
import static uk.co.real_logic.aeron.logbuffer.FrameDescriptor.frameVersion;

public abstract class UdpChannelTransport implements AutoCloseable
{
    private final UdpChannel udpChannel;
    private final LossGenerator lossGenerator;
    private final EventLogger logger;
    private final ByteBuffer receiveByteBuffer = ByteBuffer.allocateDirect(Configuration.RECEIVE_BYTE_BUFFER_LENGTH);
    private final UnsafeBuffer receiveBuffer = new UnsafeBuffer(receiveByteBuffer);
    private DatagramChannel sendDatagramChannel;
    private DatagramChannel receiveDatagramChannel;
    private SelectionKey selectionKey;
    private UdpTransportPoller transportPoller;
    private InetSocketAddress bindSocketAddress;
    private InetSocketAddress endPointSocketAddress;
    private InetSocketAddress connectAddress;

    public UdpChannelTransport(
        final UdpChannel udpChannel,
        final InetSocketAddress endPointSocketAddress,
        final InetSocketAddress bindSocketAddress,
        final InetSocketAddress connectAddress,
        final LossGenerator lossGenerator,
        final EventLogger logger)
    {
        this.udpChannel = udpChannel;
        this.lossGenerator = lossGenerator;
        this.logger = logger;
        this.endPointSocketAddress = endPointSocketAddress;
        this.bindSocketAddress = bindSocketAddress;
        this.connectAddress = connectAddress;
    }

    /**
     * Create the underlying channel for reading and writing.
     */
    public void openDatagramChannel()
    {
        try
        {
            sendDatagramChannel = DatagramChannel.open(udpChannel.protocolFamily());
            receiveDatagramChannel = sendDatagramChannel;
            if (udpChannel.isMulticast())
            {
                final NetworkInterface localInterface = udpChannel.localInterface();

                if (null != connectAddress)
                {
                    receiveDatagramChannel = DatagramChannel.open(udpChannel.protocolFamily());
                }

                receiveDatagramChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
                receiveDatagramChannel.bind(new InetSocketAddress(endPointSocketAddress.getPort()));
                receiveDatagramChannel.join(endPointSocketAddress.getAddress(), localInterface);
                sendDatagramChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, localInterface);

                if (null != connectAddress)
                {
                    sendDatagramChannel.connect(connectAddress);
                }
            }
            else
            {
                sendDatagramChannel.bind(bindSocketAddress);

                if (null != connectAddress)
                {
                    sendDatagramChannel.connect(connectAddress);
                }
            }

            if (0 != Configuration.SOCKET_SNDBUF_LENGTH)
            {
                sendDatagramChannel.setOption(StandardSocketOptions.SO_SNDBUF, Configuration.SOCKET_SNDBUF_LENGTH);
            }

            if (0 != Configuration.SOCKET_RCVBUF_LENGTH)
            {
                receiveDatagramChannel.setOption(StandardSocketOptions.SO_RCVBUF, Configuration.SOCKET_RCVBUF_LENGTH);
            }

            sendDatagramChannel.configureBlocking(false);
            receiveDatagramChannel.configureBlocking(false);
        }
        catch (final IOException ex)
        {
            throw new RuntimeException(String.format(
                "channel \"%s\" : %s", udpChannel.originalUriString(), ex.toString()), ex);
        }
    }

    /**
     * Register this transport for reading from a {@link UdpTransportPoller}.
     *
     * @param transportPoller to register read with
     */
    public void registerForRead(final UdpTransportPoller transportPoller)
    {
        this.transportPoller = transportPoller;
        selectionKey = transportPoller.registerForRead(this);
    }

    /**
     * Return underlying {@link UdpChannel}
     *
     * @return underlying channel
     */
    public UdpChannel udpChannel()
    {
        return udpChannel;
    }

    /**
     * The {@link DatagramChannel} for this transport channel.
     *
     * @return {@link DatagramChannel} for this transport channel.
     */
    public DatagramChannel receiveDatagramChannel()
    {
        return receiveDatagramChannel;
    }

    /**
     * Send contents of {@link ByteBuffer} to connected address
     *
     * @param buffer to send
     * @return number of bytes sent
     */
    public int send(final ByteBuffer buffer)
    {
        logger.logFrameOut(buffer, connectAddress);

        int byteSent = 0;
        try
        {
            byteSent = sendDatagramChannel.write(buffer);
        }
        catch (final PortUnreachableException | ClosedChannelException ex)
        {
            // ignore
        }
        catch (final IOException ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }

        return byteSent;
    }

    /**
     * Send contents of {@link java.nio.ByteBuffer} to remote address
     *
     * @param buffer        to send
     * @param remoteAddress to send to
     * @return number of bytes sent
     */
    public int sendTo(final ByteBuffer buffer, final InetSocketAddress remoteAddress)
    {
        logger.logFrameOut(buffer, remoteAddress);

        int bytesSent = 0;
        try
        {
            bytesSent = sendDatagramChannel.send(buffer, remoteAddress);
        }
        catch (final IOException ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }

        return bytesSent;
    }

    /**
     * Close transport, canceling any pending read operations and closing channel
     */
    public void close()
    {
        try
        {
            if (null != selectionKey)
            {
                selectionKey.cancel();
            }

            if (null != transportPoller)
            {
                transportPoller.cancelRead(this);
            }

            sendDatagramChannel.close();

            if (receiveDatagramChannel != sendDatagramChannel)
            {
                receiveDatagramChannel.close();
            }
        }
        catch (final Exception ex)
        {
            logger.logException(ex);
        }
    }

    /**
     * Is transport representing a multicast media or unicast
     *
     * @return if transport is multicast media
     */
    public boolean isMulticast()
    {
        return udpChannel.isMulticast();
    }

    /**
     * Return socket option value
     *
     * @param name of the socket option
     * @param   type of option
     * @return option value
     */
    public  T getOption(final SocketOption name)
    {
        T option = null;
        try
        {
            option = sendDatagramChannel.getOption(name);
        }
        catch (final IOException ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }

        return option;
    }

    /**
     * Return the capacity of the {@link ByteBuffer} used for reception
     *
     * @return capacity of receiving byte buffer
     */
    public int receiveBufferCapacity()
    {
        return receiveByteBuffer.capacity();
    }

    /**
     * Attempt to receive waiting data.
     *
     * @return number of bytes received.
     */
    public abstract int pollForData();

    public final boolean isValidFrame(final UnsafeBuffer receiveBuffer, final int length)
    {
        boolean isFrameValid = true;

        if (frameVersion(receiveBuffer, 0) != HeaderFlyweight.CURRENT_VERSION)
        {
            logger.log(EventCode.INVALID_VERSION, receiveBuffer, 0, frameLength(receiveBuffer, 0));
            isFrameValid = false;
        }
        else if (length < HeaderFlyweight.HEADER_LENGTH)
        {
            logger.log(EventCode.MALFORMED_FRAME_LENGTH, receiveBuffer, 0, length);
            isFrameValid = false;
        }

        return isFrameValid;
    }

    protected final UnsafeBuffer receiveBuffer()
    {
        return receiveBuffer;
    }

    protected final ByteBuffer receiveByteBuffer()
    {
        return receiveByteBuffer;
    }

    protected final EventLogger logger()
    {
        return logger;
    }

    protected final LossGenerator lossGenerator()
    {
        return lossGenerator;
    }

    protected final InetSocketAddress receive()
    {
        receiveByteBuffer.clear();

        InetSocketAddress address = null;
        try
        {
            address = (InetSocketAddress)receiveDatagramChannel.receive(receiveByteBuffer);
        }
        catch (final PortUnreachableException | ClosedChannelException ignored)
        {
            // do nothing
        }
        catch (final Exception ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }

        return address;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy