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

org.jvirtanen.nassau.moldudp64.MultiChannelMoldUDP64Client Maven / Gradle / Ivy

package org.jvirtanen.nassau.moldudp64;

import static org.jvirtanen.nassau.moldudp64.MoldUDP64.*;
import static org.jvirtanen.nassau.moldudp64.MoldUDP64Client.*;
import static org.jvirtanen.nassau.moldudp64.MoldUDP64ClientState.*;
import static org.jvirtanen.nio.ByteBuffers.*;

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.DatagramChannel;
import org.jvirtanen.nassau.MessageListener;

/**
 * A multi-channel implementation of a MoldUDP64 client. This implementation
 * uses separate channels for receiving downstream packets and sending
 * request packets.
 *
 * 

Using two channels is neither as simple nor as efficient as using one * channel. However, it results in the source UDP port in request packets * bearing no relation to the UDP port of the IP multicast. Therefore multiple * instances of this implementation can be listening to the same session on * the same host at the same time.

*/ public class MultiChannelMoldUDP64Client implements Closeable { /* * The following attributes are used for receiving downstream packets. */ private ByteBuffer rxBuffer; private DatagramChannel channel; private byte[] session; /* * The following attributes are used for sending request packets and * receiving response packets. */ private ByteBuffer requestRxBuffer; private ByteBuffer requestTxBuffer; private DatagramChannel requestChannel; private byte[] requestSession; /* * Downstream packet reception and response packet reception can run on * separate threads. */ private Object lock; private MessageListener listener; private MoldUDP64ClientStatusListener statusListener; private long nextExpectedSequenceNumber; private long nextEstimatedSequenceNumber; private MoldUDP64ClientState state; /** * Create a client. The underlying datagram channel can be either blocking * or non-blocking. The underlying datagram channel for requests can also * be either blocking or non-blocking, but it must be connected. * * @param channel the underlying datagram channel * @param requestChannel the underlying datagram channel for requests * @param listener the inbound message listener * @param statusListener the inbound status event listener */ public MultiChannelMoldUDP64Client(DatagramChannel channel, DatagramChannel requestChannel, MessageListener listener, MoldUDP64ClientStatusListener statusListener) { this.rxBuffer = ByteBuffer.allocate(RX_BUFFER_LENGTH); this.channel = channel; this.session = new byte[SESSION_LENGTH]; this.requestRxBuffer = ByteBuffer.allocate(RX_BUFFER_LENGTH); this.requestTxBuffer = ByteBuffer.allocate(HEADER_LENGTH); this.requestChannel = requestChannel; this.requestSession = new byte[SESSION_LENGTH]; this.lock = new Object(); this.listener = listener; this.statusListener = statusListener; this.nextExpectedSequenceNumber = 1; this.nextEstimatedSequenceNumber = 1; this.state = UNKNOWN; } /** * Get the underlying datagram channel. * * @return the underlying datagram channel */ public DatagramChannel getChannel() { return channel; } /** * Get the underlying datagram channel for requests. * * @return the underlying datagram channel for requests */ public DatagramChannel getRequestChannel() { return requestChannel; } /** * Receive a downstream packet. * * @see SingleChannelMoldUDP64Client#receive * @throws IOException if an I/O error occurs */ public void receive() throws IOException { rxBuffer.clear(); if (channel.receive(rxBuffer) == null) return; rxBuffer.flip(); receive(rxBuffer, session); } /** * Receive a response packet. * * @see SingleChannelMoldUDP64Client#receive * @throws IOException if an I/O error occurs */ public void receiveResponse() throws IOException { requestRxBuffer.clear(); if (requestChannel.receive(requestRxBuffer) == null) return; requestRxBuffer.flip(); receive(requestRxBuffer, requestSession); } private void receive(ByteBuffer buffer, byte[] session) throws IOException { if (buffer.remaining() < 20) throw truncatedPacket(); buffer.order(ByteOrder.BIG_ENDIAN); buffer.get(session); long sequenceNumber = buffer.getLong(); int messageCount = getUnsignedShort(buffer); boolean endOfSession = messageCount == MESSAGE_COUNT_END_OF_SESSION; if (endOfSession) messageCount = 0; long nextSequenceNumber = sequenceNumber + messageCount; synchronized (lock) { nextEstimatedSequenceNumber = Math.max(nextEstimatedSequenceNumber, nextSequenceNumber); if (sequenceNumber > nextExpectedSequenceNumber) { int requestedMessageCount = (int)Math.min(nextEstimatedSequenceNumber - nextExpectedSequenceNumber, MAX_MESSAGE_COUNT); if (state == SYNCHRONIZED) state(GAP_FILL); else if (state == UNKNOWN) state(BACKFILL); request(requestedMessageCount); return; } if (state != SYNCHRONIZED && nextEstimatedSequenceNumber == nextSequenceNumber) state(SYNCHRONIZED); if (endOfSession) { statusListener.endOfSession(); } else { while (sequenceNumber < nextExpectedSequenceNumber) { skip(buffer); sequenceNumber++; } while (nextExpectedSequenceNumber < nextSequenceNumber) { read(buffer, listener); nextExpectedSequenceNumber++; } } statusListener.downstream(); } } private void state(MoldUDP64ClientState next) throws IOException { state = next; statusListener.state(next); } private void request(int requestedMessageCount) throws IOException { requestTxBuffer.clear(); requestTxBuffer.put(session); requestTxBuffer.putLong(nextExpectedSequenceNumber); putUnsignedShort(requestTxBuffer, requestedMessageCount); requestTxBuffer.flip(); while (requestChannel.write(requestTxBuffer) == 0); statusListener.request(nextExpectedSequenceNumber, requestedMessageCount); } /** * Close the underlying datagram channel. * * @throws IOException if an I/O error occurs */ @Override public void close() throws IOException { channel.close(); requestChannel.close(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy