OscServer.java :  » Messenger » throng » flosc » Java Open Source

Java Open Source » Messenger » throng 
throng » flosc » OscServer.java
package flosc;

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

/**
 * OscServer <BR><BR> OpenSoundControl UDP Server for Gateway. Based on CommServer by Derek Clayton and dumpOSC by Matt Wright. Thanks to Jesse Gilbert (j@resistantstrain.net) for helping me with the static socket.
 * @author    Ben Chun        ben@benchun.net
 * @author    Ignacio Delgado   idelgado@h-umus.it
 * @version   2.0
 */

public class OscServer extends Thread 
{
    private DatagramSocket oscSocket;       // incoming UDP socket
    private int port;                       // incoming UDP port
    private Gateway gateway;                // Gateway for this server
    private static OscSocket outSocket;     // outgoing UDP socket
    
    private String oscOutputIP;
    private int oscOutputPort;
    
    static 
    {
        try
      {
          outSocket = new OscSocket();
      }
        catch( SocketException se )
      {
          se.printStackTrace();
      }
    }
    
    /**
     * Constructor for the OscServer.
     *
     * @param   port     UDP port for OSC communication.
     * @param   gateway  parent Gateway.
    */
    public OscServer(int port, Gateway gateway) {
    this.port = port;
    this.gateway = gateway;
    }
    public OscServer(int port, Gateway gateway, String oscOutputIp, int oscOutputPort) {
      this.port = port;
    this.gateway = gateway;
    this.oscOutputIP = oscOutputIp;
      this.oscOutputPort = oscOutputPort;
    }

    /**
     * Thread run method.  Monitors incoming messages.
    */  
    public void run() 
    {
      try{
        oscSocket = new DatagramSocket(port);
        
        Debug.writeActivity("UDP OSC server started on port: " + port,this);
        while(true) 
        {
          try{
            byte[] datagram  = new byte[4096];
            DatagramPacket packet = new DatagramPacket(datagram, datagram.length);
    
          // block until a datagram is received
          oscSocket.receive(packet);
      
          // parse the packet
          OscPacket oscp = new OscPacket();
          oscp.address = packet.getAddress();
          oscp.port = packet.getPort();
          parseOscPacket(datagram, packet.getLength(), oscp);
        
          gateway.multiplexPacket(oscp, this);
          }catch(SocketException exception){
              Debug.writeActivity(exception.getMessage(),this);
              break;
            }catch(IOException ioe){
                Debug.writeException("Server error...",this, ioe);
                // kill this server
            }catch(Exception ex) {
            Debug.writeException("Exception while sending...",this, ex);
          }
      }
          
      }catch(SocketException exception){
        Debug.writeActivity("Server error...could not create socket: "+exception.getMessage(),this);
      }
    }

    /**
     * This method is based on dumpOSC::ParseOSCPacket.  It verifies
     * that the packet is well-formed and puts the data into an
     * OscPacket object, or else prints useful error messages about
     * what went wrong.
     *
     * @param   datagram  a byte array containing the OSC packet
     * @param   n         size of the byte array
     * @param   packet    the OscPacket object to put data into
     */
    public void parseOscPacket(byte[] datagram, int n, OscPacket packet) 
    {
    int size, i;
  
    if ( (n % 4) != 0 ) 
    {
        Debug.writeActivity("SynthControl packet size (" + n +
          ") not a multiple of 4 bytes, dropped it.",this);
        return;
    }
  
    String dataString = new String(datagram);
    if ( ( n >= 8 ) && dataString.startsWith( "#bundle" ) ) 
    {
        /* This is a bundle message. */
        
        if ( n < 16 ) 
        {
        Debug.writeActivity("Bundle message too small (" + n +
                " bytes) for time tag, dropped it.",this);
        return;
        }
  
        /* Get the time tag */
        Long time = new Long( Bytes.toLong( Bytes.copy(datagram, 8, 8) ) );
        packet.setTime(time.longValue());
  
        i = 16; /* Skip "#bundle\0" and time tags */
        while( i<n ) 
        {
        size = ( Bytes.toInt( Bytes.copy(datagram, i, i+4 ) ) );
        if ((size % 4) != 0) 
        {
            Debug.writeActivity("Bad size count" + size +
              "in bundle (not a multiple of 4)",this);
            return;
        }
        
        if ( (size + i + 4) > n ) 
        {
            Debug.writeActivity("Bad size count" + size + "in bundle" +
              "(only" + (n-i-4) + "bytes left in entire bundle)",this);
            return;
        }
        
        /* Recursively handle element of bundle */
        byte[] remaining =  Bytes.copy(datagram, i+4);
        parseOscPacket(remaining, size, packet);
        i += (4 + size);
        }  
    }else{
        /* This is not a bundle message */      
        Vector nameAndData = getStringAndData(datagram, n);
  
        String name = (String) nameAndData.firstElement();
        OscMessage message = new OscMessage(name);
  
        byte[] data = (byte[]) nameAndData.lastElement();
        Vector[] typesAndArgs = getTypesAndArgs(data);
        
        if(typesAndArgs != null){
          message.setTypesAndArgs(typesAndArgs[0], typesAndArgs[1]);
          packet.addMessage(message);
        }else{
          packet = null;
        }
    }
    }

    /**
     * Takes a byte array starting with a string padded with null
     * characters so that the length of the entire block is a multiple
     * of 4, and seperates it into a String and a byte array of the
     * remaining data.  These are then returned in a Vector.
     *
     * @param   block           block of data beginning with a string
     * @param   stringLength    number of characters in the string
    */  
    public Vector getStringAndData(byte[] block, int stringLength) 
    {
    Vector v = new Vector();
    int i;
    
    if ( stringLength %4 != 0) 
    {
        Debug.writeActivity("printNameAndArgs: bad boundary",this);
        return v;
    }
  
    for (i = 0; block[i] != '\0'; i++) 
    {
        if (i >= stringLength) 
        {
        Debug.writeActivity("printNameAndArgs: Unreasonably long string",this);
        return v;
        }
    }
    // v.firstElement() is the String
    v.addElement( new String(Bytes.copy(block, 0, i)) );
  
    i++;
    for (; (i % 4) != 0; i++) 
    {
        if (i >= stringLength) 
        {
        Debug.writeActivity("printNameAndArgs: Unreasonably long string",this);
        return v;
        }
        
        if (block[i] != '\0') 
        {
        Debug.writeActivity("printNameAndArgs: Incorrectly padded string.",this);
        return v;
        }
    }
    // v.elementAt(1) is the position in the orginal byte[] where the data starts
    v.addElement( new Integer(i) );
    // v.lastElement() is the byte[] of data
    v.addElement( Bytes.copy( block, i ) );
    return v;
    }

    /**
     * Returns an array of Vectors containing types and arguments.
     *
     * @param   block   byte array containing types and arguments
    */
    public Vector[] getTypesAndArgs( byte[] block ) 
    {
    // TBD : throw exceptions or something when there are no type tags
    int n = block.length;
    Vector[] va = new Vector[2];
    if (n != 0) 
    {
        if (block[0] == ',') 
        {
        if (block[1] != ',') 
        {
            /* This message begins with a type-tag string */
            va = getTypeTaggedArgs( block );
        } 
        else 
        {
            /* Double comma means an escaped real comma, not a
             * type string */
            va = getHeuristicallyTypeGuessedArgs( block );
        }
        
        } 
        else 
        {
          va = getHeuristicallyTypeGuessedArgs( block );
        }
    }
    return va;
    }

    /**
     * Returns Vectors containing the types and arguments from a
     * type-tagged byte array
     *
     * @param   block   a byte array with type-tagged data
    */
    public Vector[] getTypeTaggedArgs( byte[] block ) 
    {
    Vector<Character> typeVector = new Vector<Character>();
    Vector argVector = new Vector();
    int p = 0;
  
    /* seperate the block into the types byte array and the
     * argument byte array*/
    Vector typesAndArgs = getStringAndData(block, block.length);
    byte[] args = (byte[]) typesAndArgs.lastElement();
    
    for (int thisType=1; block[thisType] != 0; thisType++) 
    {
        switch (block[thisType]) 
        {
  
          case '[' :
        typeVector.addElement(new Character('['));
        break;
    
          case ']' :
        typeVector.addElement(new Character(']'));
        break;
    
          case 'i': case 'r': case 'm': case 'c':
        typeVector.addElement(new Character('i'));
        argVector.addElement( new Integer( Bytes.toInt( Bytes.copy(args, p, p+4) )) );
        p += 4;
        break;
        
          case 'f':
        typeVector.addElement(new Character('f'));
        argVector.addElement( new Float( Bytes.toFloat( Bytes.copy(args, p, p+4) )) );
        p += 4;
        break;
        
          case 'h': case 't':
        typeVector.addElement(new Character('h'));
        argVector.addElement( new Long( Bytes.toLong( Bytes.copy(args, p, p+8) )) );
        p += 8;
        break;
        
          case 'd':
        typeVector.addElement(new Character('d'));
        argVector.addElement( new Double( Bytes.toDouble( Bytes.copy(args, p, p+8) )) );
        p += 8;
        break;
        
          case 's': case 'S':
        typeVector.addElement(new Character('s'));
        byte[] remaining = Bytes.copy(args, p);
        Vector v = getStringAndData( remaining, remaining.length );
        argVector.addElement( (String)v.firstElement() );
        p += ((Integer)v.elementAt(1)).intValue();
        break;
        
          case 'T':
        typeVector.addElement(new Character('T')); 
        break;
        
          case 'F':
        typeVector.addElement(new Character('F'));
        break;
        
          case 'N':
        typeVector.addElement(new Character('N'));
        break;
        
          case 'I':
        typeVector.addElement(new Character('I'));
        break;
    
          default:
        Debug.writeActivity( "[Unrecognized type tag " +
                 block[thisType] + "]" ,this);
        }
    }
  
    Vector[] returnValue = new Vector[2];
    returnValue[0] = typeVector;
    returnValue[1] = argVector;
    return returnValue;
    }
  

    /**
     * Returns the arguments from a non-type-tagged byte array
     *
     * @param   block   a byte array containing data
    */
    public Vector[] getHeuristicallyTypeGuessedArgs( byte[] block ) 
    {
    // TBD : handle packets without type tags
    Debug.writeActivity("Bad OSC packet: No type tags",this);
    return new Vector[2];
    }


   /**
     * Sends an OscPacket via an OscSocket.  the packet contains the
     * address and port information.
     *
     * @param packet  the OscPacket to send
     */
    public void sendPacket(OscPacket packet) 
    {
    try 
    {
        Debug.writeActivity("OscServer sending OSC packet via UDP to IP "+oscOutputIP+" on port "+oscOutputPort,this);
        outSocket.sendToOutputPortAndIP(packet, InetAddress.getByName(oscOutputIP), oscOutputPort);
    } 
    catch(IOException ioe) 
    {
      Debug.writeException("sendPacket error...",this, ioe);
        } catch(Exception ex) {
        Debug.writeException("Exception while sending...",this, ex);
      }
    }
    
    public void sendPacketToPort(OscPacket packet, int port) {
      try {
          Debug.writeActivity("OscServer sending OSC packet via UDP to localhost on port "+port,this);
          outSocket.sendToOutputPort(packet, port);
      } catch(IOException ioe) {
        Debug.writeException("sendPacket error...",this, ioe);
        } catch(Exception ex) {
        Debug.writeException("Exception while sending...",this, ex);
      }
    }
    
    public void sendPacketToPortAndIP(OscPacket packet, String ip, int port) {
      try {
          Debug.writeActivity("OscServer sending OSC packet via UDP to IP "+ ip + " on port "+port,this);
          outSocket.sendToOutputPortAndIP(packet, InetAddress.getByName(ip), port);
      } catch(IOException ioe) {
            Debug.writeException("sendPacket error...",this, ioe);
      } catch(Exception ex) {
        Debug.writeException("Exception while sending...",this, ex);
      }
    }


    /**
     * Stops the UDP server.
    */
    public void killServer() 
    {
    oscSocket.close();
    Debug.writeActivity("UDP OSC server stopped",this);
    }
    public int getPort(){
      return port;
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.