Package rtpi.transport.ipmc

Source Code of rtpi.transport.ipmc.IPMCTransport

/* The Java RTP/I library, Version 0.1 alpha.
* This library provides the functionality of RTP/I as it is specified in
* Internet Draft draft-mauve-rtpi-00.txt.
*
* Copyright (C) 2000 Martin Mauve
* University of Mannheim / Germany
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Martin Mauve
*
* e-mail:
* mauve@informatik.uni-mannheim.de
*
* paper mail:
* Martin Mauve
* University of Mannheim
* Lehrstuhl Praktische Informatik IV
* L15, 16
* 68131 Mannheim
* Germany
*/

package rtpi.transport.ipmc;

import rtpi.transport.Transport;
import rtpi.transport.TransportRecipient;
import rtpi.transport.TransportPacket;
import rtpi.transport.NotificationRecipient;

import java.net.*;
import java.io.IOException;
import java.util.Vector;

import rtpi.util.SyncQueue;
import rtpi.util.Sleeper;
import rtpi.util.WakeUpThread;
import rtpi.util.Sleeper2;
import rtpi.util.WakeUpThread2;

/**
* IPMCTransport is a Transport implementation for (unreliable) IP multicast
* with minimal rate control. If IPMCTransort receives more transport packets
* from the local application, than can be sent according to the given rate,
* then packets are bufferd and delayed. The actual rate should never exceed
* the given rate. Currently this implementation cannot handle more than
* 4 MBit/s. The maximum data rate depends on time resolution (50ms for
* Win95/98 machines) and the (ethernet) MTU.
*/

public class IPMCTransport extends Thread implements Transport, Sleeper, Sleeper2 {

    SyncQueue messages = new SyncQueue();

    MulticastSocket socket;
    InetAddress address;
    int port;
    int ttl;
    TransportRecipient recipient;
    IPMCTransportListener listener;

    int transmissionRate;
    long sleepTime;
    long waterHigh;
    long watermark;
    long waterAccumulatedSince = 0;

    boolean waiting = false;
    Vector waitingPackets = new Vector(100,50);
    WakeUpThread alarm=null;

    int bytesSentLastInterval=0;
    int bytesReceivedLastInterval=0;
    NotificationRecipient notifyWho=null;
    int acceptableSendRate=0;
    int acceptableReceiveRate=0;

    WakeUpThread2 bookKeepThread = null;
    static final long BOOK_KEEP_INTERVAL = 500; // The interval with which the bookkeeping is done!

    static final int MTU=1500;                 // Ethernet MTU
    static final int TRANSPORT_HEADER_SIZE=100; // generous approximation
    static final int MAX_PAYLOAD_SIZE=1400;     // Subtract size of header info
    static final int MIN_BUF_SIZE=64000;         // this is the minimum  buffer we expect to get for
    ;                                           // a socket!

    static final int HEADER_SIZE=28; //IP=20 UDP=8;


    /**
     * This constructs an IPMCTransport instance.
     *
     * @param taddr The multicast (network) address of this transport instance.
     * @param tport The port for this transport address.
     * @param tttl The time to live for the transport packets sent.
     * @param rate The maximum rate for this transport instance.
     */

    public IPMCTransport(InetAddress taddr, int tport, int tttl, int rate) {
  address = taddr;
  port = tport;
  ttl = tttl;
 
  execSetRate(rate);
  bookKeepThread = new WakeUpThread2(BOOK_KEEP_INTERVAL, this);
  start();
    }

    public void run() {
 
  Message message=null;

  while (true) {
     
      try {
    message=(Message) messages.get();
      } catch (Exception exe) {
    System.err.println("RTQueue : could not get next message!");
    continue;
      }
     
      switch(message.opCode) {
      case Message.QUIT:
    execQuit(); // clean up!
    return; // return to leave the run method!
      case Message.JOIN_GROUP:
    execJoinGroup();
    break;
      case Message.LEAVE_GROUP:
    execLeaveGroup();
    break;
      case Message.REGISTER:
    execRegisterTransportRecipient((TransportRecipient) message.args);
    break;
      case Message.SEND:
    execSendTransportPacket((TransportPacket) message.args);
    break;
      case Message.SET_RATE:
    execSetRate(((Integer) message.args).intValue());
    break;
      case Message.WAKE_UP:
    execWakeUp();
    break;
      case Message.WAKE_UP2:
    execWakeUp2();
    break;
      case Message.QUERY_NETWORK_RESOURCES:
    QueryNetworkResourcesArgs args0 = (QueryNetworkResourcesArgs) message.args;
    execQueryNetworkResources(args0.senderFree, args0.receiverFree, args0.recipient);
    break;
      case Message.UPDATE_RECEIVED_RATE:
    execUpdateReceivedRate(((Integer) message.args).intValue());
    break;

      default:
    System.err.println("IPMCTransport: illegal message opcode: "+message.opCode+message.args);
    break;
      }
     
  }
    }


    /**
     * This terminates the reliability service.
     */

    public void quit(){
  messages.put(new Message(Message.QUIT, null));
    }

    private void execQuit() {
  try {
      if (alarm!=null) {
    alarm.interrupt();
      }
      if (bookKeepThread!=null) {
    bookKeepThread.interrupt();
      }
      listener.leaveGroup();
      socket.close();
  } catch (Exception ex) {
      System.err.println("IPMCTransport: Could not execute quit properly, there may me resources remianing. Error was "+ex);
  }
  return;
    }

    /**
     * This makes the IPMCTransport instrance join the MC group.
     */

    public void joinGroup() throws IOException {
  if (!address.isMulticastAddress())
      throw new IOException();
  messages.put(new Message(Message.JOIN_GROUP, null));
    }

    void execJoinGroup() {
  try {
      socket=new MulticastSocket(port);
      socket.setTimeToLive(ttl);
      socket.joinGroup(address);
      socket.setSendBufferSize(MIN_BUF_SIZE);
      socket.setReceiveBufferSize(MIN_BUF_SIZE);
      if (socket.getSendBufferSize()<MIN_BUF_SIZE ||
    socket.getReceiveBufferSize()<MIN_BUF_SIZE) {
    System.err.println("IPMCTransport: Socketbuffers too small,");
    System.err.println("might drop packets locally!");
      }
  } catch (Exception ex) {
      System.err.println("IPMCTransport: cant execJoin!");
      return;
  }

  listener = new IPMCTransportListener(recipient, socket, this);
  listener.start();
    }

    /**
     * This makes the IPMCTransport instance leave the MC group.
     */

    public void leaveGroup() {
  messages.put(new Message(Message.LEAVE_GROUP, null));
   

    void execLeaveGroup() {
  listener.leaveGroup();

  try {
      socket.leaveGroup(address);
      socket.close();
  } catch (IOException ex) {
      System.err.println("IPMCTransport: cant execLeave!");
      return;
  }

  socket=null;
    }
   

    /**
     * Register a recipient for the transport packets received via
     * this IPMC transport instance. Note that there can be only
     * one recipient. If a second recipient is registerd, this
     * second recipient will be the only one packets are delivered
     * to.
     *
     * @param transRec The recipient which should receive transport packets.
     */

    public void registerTransportRecipient(TransportRecipient transRec) {
  messages.put(new Message(Message.REGISTER, transRec));
    }
   
    void execRegisterTransportRecipient(TransportRecipient transRec) {
  recipient=transRec;
    }
   
    /**
     * Send a transport packet.
     *
     * @param packet The TransportPacket to be sent.
     */

    public void sendTransportPacket(TransportPacket packet) {
  messages.put(new Message(Message.SEND, packet));
    }
 
    void execSendTransportPacket(TransportPacket packet) {
  if (waiting) {
      waitingPackets.add(packet);
      return;
  }
 
  watermark+=packet.getLength()+TRANSPORT_HEADER_SIZE;
 
  if (watermark>=waterHigh) {
      if ((System.currentTimeMillis()-waterAccumulatedSince) >= sleepTime) {
    watermark=packet.getLength()+TRANSPORT_HEADER_SIZE;
    waterAccumulatedSince=System.currentTimeMillis();
      } else {
    waitingPackets.add(0,packet);
    alarm = new WakeUpThread(sleepTime,this);
    watermark=0;
    waterAccumulatedSince=0;
    waiting=true;
      }
  }

  if(!waiting) try {
      DatagramPacket dPacket=new DatagramPacket(packet.getData(),0,
                 packet.getLength(), address, port);
      socket.send(dPacket);

      bytesSentLastInterval+=packet.getLength();
     
  } catch (Exception ex) {
      System.err.println("IPMCTransport: cant sendPacket: "+ex+" has the session been joined?");
      return;
  }
    }
   

    /**
     * The maximum rate to be sent over this IPMCTransport instance.
     *
     * @param rate The maximum rate.
     */

    public void setRate(int rate) {
  messages.put(new Message(Message.SET_RATE, new Integer(rate)));
    }
   
    void execSetRate(int rate) {
 
  // max rate we can handle is 4 MBit/s
  // with more we'll overrun our socket buffer!
  // We could sleep less than 50 ms, however this
  // is the time resolution on windows machines.
 
  if (rate>4000000) {
      transmissionRate=4000000;
  } else {
      transmissionRate=rate;
  }
 
  // we should be able to transmit at least one
  // message before having to sleep!
 
  long minSleep = 1000*MTU*8;
  minSleep = minSleep/transmissionRate+1; // +1 because fraction is cut off
 
  if (minSleep>50) {
      sleepTime=minSleep;
  } else {
      sleepTime=50;
  }
 
  waterHigh=(sleepTime*transmissionRate)/8000+1; // +1 because fraction is cut off,
                                                 // 8000 to transfer bit*ms to byte*sec
    }

    public void wakeUp() {
  messages.put(new Message(Message.WAKE_UP, null));
    }

    void execWakeUp() {

  TransportPacket packet;

  int i= 0;

  waiting=false;

  while (!waiting && !waitingPackets.isEmpty()) {

      try {
    packet = (TransportPacket) waitingPackets.remove(0);
      } catch (ArrayIndexOutOfBoundsException ex) {
    System.err.println("IPMCTransport: could not read waiting packet!");
    return;
      }

      watermark+=packet.getLength()+TRANSPORT_HEADER_SIZE;
     
      if (watermark>=waterHigh) {
    waitingPackets.add(0,packet);
    alarm = new WakeUpThread(sleepTime,this);
    watermark=0;
    waiting=true;
      }
     
      if (!waiting) try {
    i++;
    DatagramPacket dPacket=new DatagramPacket(packet.getData(), 0,
                packet.getLength(), address, port);
    socket.send(dPacket);
    bytesSentLastInterval+=packet.getLength();

      } catch (IOException ex) {
    System.err.println("IPMCTransport: cant sendPacket!");
    return;
      }
  }
 
  if (!waiting) {
      waterAccumulatedSince=System.currentTimeMillis();
  }
 
    }


    void updateReceivedRate(int length) {
  messages.put(new Message(Message.UPDATE_RECEIVED_RATE, new Integer(length)));
    }

    private void execUpdateReceivedRate(int length) {
  bytesReceivedLastInterval+=length;
   

    public void wakeUp2() {
  messages.put(new Message(Message.WAKE_UP2, null));
    }

    void execWakeUp2() {
  if ((notifyWho!=null) &&
      (acceptableSendRate >= (bytesSentLastInterval*8*1000/BOOK_KEEP_INTERVAL)) &&
      (acceptableReceiveRate >= (bytesReceivedLastInterval*8*1000/BOOK_KEEP_INTERVAL))) {
      notifyWho.networkResourcesAvailable();
      notifyWho=null;
  }

  bytesSentLastInterval=0;
  bytesReceivedLastInterval=0;
  bookKeepThread = new WakeUpThread2(BOOK_KEEP_INTERVAL, this);
    }
 
    /**
     * This requests that a NotificationRecipient is informed when the
     * network becomes idle.
     *
     * @param sendFree The boundary of the send data rate (bit/s) below which the network
     *                   is considered idle.
     * @param receivFree The boundary of of the received data rate (bit/s) below which the
     *                   network is considered idle. This includes the data send. Therefore:
     *                   receivedFree=sendFree+x 
     * @param  recipient The notificationRecipient that is interested in receiving a
     *                   notification when the network becomes idle.
     */

    public void queryNetworkResources(int senderFree, int receiverFree, NotificationRecipient recipient) {
  messages.put(new Message(Message.QUERY_NETWORK_RESOURCES, new QueryNetworkResourcesArgs( senderFree,
                         receiverFree,
                         recipient)));
    }

    void execQueryNetworkResources(int senderFree, int receiverFree, NotificationRecipient recipient) {
  acceptableSendRate = senderFree;
  acceptableReceiveRate = receiverFree;
  notifyWho=recipient;
    }

   
    /**
     * This queries the maximum transport payload size, which can be transported
     * without fragmentation.
     *
     * @return The maximum IPMCTransport payload size.
     */
    public int getTransportPayloadSize() {
  return MAX_PAYLOAD_SIZE;
    }

    /**
     * This queries the header size (transport+network).
     *
     * @return The header size.
     */
    public int getHeaderSize() {
  return HEADER_SIZE;
    }
}










TOP

Related Classes of rtpi.transport.ipmc.IPMCTransport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.