Added a SSDP Server and Client

This commit is contained in:
Ziver Koc 2010-02-04 19:26:32 +00:00
parent 91b6c2bf85
commit 49bb25a4d7
6 changed files with 359 additions and 96 deletions

View file

@ -151,16 +151,11 @@ class MathNumber extends Math{
abstract class MathOperation extends Math{
Math math1;
Math math2;
int priority;
public abstract double exec();
}
class MathAddition extends MathOperation{
public MathAddition(){
priority = 1;
}
public double exec() {
return math1.exec() + math2.exec();
}
@ -171,10 +166,6 @@ class MathAddition extends MathOperation{
}
class MathSubtraction extends MathOperation{
public MathSubtraction(){
priority = 1;
}
public double exec() {
return math1.exec() - math2.exec();
}
@ -185,10 +176,6 @@ class MathSubtraction extends MathOperation{
}
class MathMultiplication extends MathOperation{
public MathMultiplication(){
priority = 2;
}
public double exec() {
return math1.exec() * math2.exec();
}
@ -199,10 +186,6 @@ class MathMultiplication extends MathOperation{
}
class MathDivision extends MathOperation{
public MathDivision(){
priority = 2;
}
public double exec() {
return math1.exec() / math2.exec();
}
@ -213,10 +196,6 @@ class MathDivision extends MathOperation{
}
class MathModulus extends MathOperation{
public MathModulus(){
priority = 2;
}
public double exec() {
return math1.exec() % math2.exec();
}
@ -227,10 +206,6 @@ class MathModulus extends MathOperation{
}
class MathPow extends MathOperation{
public MathPow(){
priority = 3;
}
public double exec() {
double ret = 1;
double tmp1 = math1.exec();

View file

@ -12,17 +12,18 @@ public class HTTPHeaderParser {
private static final Pattern equalPattern = Pattern.compile("=");
private static final Pattern andPattern = Pattern.compile("&");
private static final Pattern semiColonPattern = Pattern.compile(";");
// HTTP info
private String type;
private String url;
private HashMap<String, String> url_attr;
private float version;
private int httpCode;
// params
private HashMap<String, String> attributes;
private HashMap<String, String> cookies;
/**
* Parses the HTTP header information from the stream
*
@ -33,7 +34,7 @@ public class HTTPHeaderParser {
url_attr = new HashMap<String, String>();
attributes = new HashMap<String, String>();
cookies = new HashMap<String, String>();
String tmp = null;
if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
parseStartLine( tmp );
@ -43,7 +44,7 @@ public class HTTPHeaderParser {
}
parseCookies();
}
/**
* Parses the HTTP header information from an String
*
@ -53,7 +54,7 @@ public class HTTPHeaderParser {
url_attr = new HashMap<String, String>();
attributes = new HashMap<String, String>();
cookies = new HashMap<String, String>();
Scanner sc = new Scanner(in);
sc.useDelimiter("\n");
String tmp = null;
@ -65,7 +66,7 @@ public class HTTPHeaderParser {
}
parseCookies();
}
/**
* Parses the first header line and ads the values to
* the map and returns the file name and path
@ -75,21 +76,29 @@ public class HTTPHeaderParser {
* @return The path and file name as a String
*/
protected void parseStartLine(String line){
type = (line.substring(0, line.indexOf(" "))).trim();
version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() );
line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim();
// parse URL and attributes
if(line.indexOf('?') > -1){
url = line.substring(0, line.indexOf('?'));
line = line.substring(line.indexOf('?')+1, line.length());
parseUrlAttributes(line, url_attr);
// Server Response
if( line.startsWith("HTTP/") ){
version = Float.parseFloat( line.substring( 5 , 8) );
httpCode = Integer.parseInt( line.substring( 9, 12 ));
}
// Client Request
else{
url = line;
type = (line.substring(0, line.indexOf(" "))).trim();
version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() );
line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim();
// parse URL and attributes
if(line.indexOf('?') > -1){
url = line.substring(0, line.indexOf('?'));
line = line.substring(line.indexOf('?')+1, line.length());
parseUrlAttributes(line, url_attr);
}
else{
url = line;
}
}
}
/**
* Parses a String with variables from a get or post
* that was sent from a client and puts the data into a HashMap
@ -120,7 +129,7 @@ public class HTTPHeaderParser {
data[0].trim().toUpperCase(), // Key
(data.length>1 ? data[1] : "").trim()); //Value
}
/**
* Parses the attribute "Cookie" and returns a HashMap
* with the values
@ -152,6 +161,12 @@ public class HTTPHeaderParser {
public float getHTTPVersion(){
return version;
}
/**
* @return the HTTP Return Code from a Server
*/
public float getHTTPCode(){
return httpCode;
}
/**
* @return the URL that the client sent the server
*/
@ -179,8 +194,8 @@ public class HTTPHeaderParser {
public String getCookie(String name){
return cookies.get( name );
}
/**
* @return athe parsed cookies
*/
@ -199,39 +214,39 @@ public class HTTPHeaderParser {
public HashMap<String, String> getAttributes(){
return attributes;
}
public String toString(){
StringBuffer tmp = new StringBuffer();
tmp.append("\nType: ");
tmp.append("Type: ");
tmp.append(type);
tmp.append("\nHTTP Version: HTTP/");
tmp.append(version);
tmp.append("\nURL: ");
tmp.append(url);
for( String key : url_attr.keySet() ){
tmp.append("\nURL Attr: ");
tmp.append(key);
tmp.append("=");
tmp.append( url_attr.get(key) );
}
for( String key : attributes.keySet() ){
tmp.append("\nHTTP Attr: ");
tmp.append(key);
tmp.append("=");
tmp.append( attributes.get(key) );
}
for( String key : cookies.keySet() ){
tmp.append("\nCookie: ");
tmp.append(key);
tmp.append("=");
tmp.append( cookies.get(key) );
}
return tmp.toString();
}
}

View file

@ -0,0 +1,189 @@
package zutil.network.ssdp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.LinkedList;
import zutil.MultiPrintStream;
import zutil.network.http.HTTPHeaderParser;
import zutil.network.http.HttpPrintStream;
import zutil.network.threaded.ThreadedUDPNetwork;
import zutil.network.threaded.ThreadedUDPNetworkThread;
import zutil.wrapper.StringOutputStream;
/**
* An SSDP client class that will request
* service information.
*
* @author Ziver
*/
public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
// Contains all the received services
private HashMap<String, LinkedList<SSDPServiceInfo>> services_st;
private HashMap<String, SSDPServiceInfo> services_usn;
public static void main(String[] args) throws IOException{
SSDPClient ssdp = new SSDPClient();
ssdp.requestService("upnp:rootdevice");
ssdp.start();
for(int i=0; true ;++i){
while( i==ssdp.getServicesCount("upnp:rootdevice") ){ try{Thread.sleep(100);}catch(Exception e){} }
MultiPrintStream.out.println( "************************" );
MultiPrintStream.out.println( ssdp.getServices("upnp:rootdevice").get(i) );
}
}
/**
* Creates new instance of this class. An UDP
* listening socket at the SSDP port.
*
* @throws IOException
*/
public SSDPClient() throws IOException{
super( null );
super.setThread( this );
services_st = new HashMap<String, LinkedList<SSDPServiceInfo>>();
services_usn = new HashMap<String, SSDPServiceInfo>();
}
/**
* Sends an request for an service
*
* @param st is the SearchTarget of the service
*
* ***** REQUEST:
* M-SEARCH * HTTP/1.1
* Host: 239.255.255.250:reservedSSDPport
* Man: "ssdp:discover"
* ST: ge:fridge
* MX: 3
*
*/
public void requestService(String st){
try {
services_st.put( st, new LinkedList<SSDPServiceInfo>() );
// Generate an SSDP discover message
StringOutputStream msg = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
http.setRequestType("M-SEARCH");
http.setRequestURL("*");
http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR+":"+SSDPServer.SSDP_PORT );
http.setHeader("ST", st );
http.setHeader("Man", "\"ssdp:discover\"" );
http.setHeader("MX", "3" );
http.close();
//MultiPrintStream.out.println("***** REQUEST: \n"+msg);
byte[] data = msg.toString().getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length,
InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ),
SSDPServer.SSDP_PORT );
super.send( packet );
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns a list of received services by
* the given search target.
*
* @param st is the search target
* @return a list of received services
*/
public LinkedList<SSDPServiceInfo> getServices(String st){
return services_st.get( st );
}
/**
* Returns the amount of services in the search target
*
* @param st is the search target
* @return the amount of services
*/
public int getServicesCount(String st){
if( services_st.containsKey( st ) ){
return services_st.get( st ).size();
}
return 0;
}
/**
* Returns a service with the given USN.
*
* @param usn is the unique identifier for the service
* @return an service, null if there is no such service
*/
public SSDPServiceInfo getService(String usn){
return services_usn.get( usn );
}
/**
* Clears all the received information of the services
*/
public void clearServices(){
services_usn.clear();
services_st.clear();
}
/**
* Waits for responses
*
* ***** RESPONSE;
* HTTP/1.1 200 OK
* Ext:
* Cache-Control: no-cache="Ext", max-age = 5000
* ST: ge:fridge
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
* Location: http://localhost:80
*/
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
HTTPHeaderParser header = new HTTPHeaderParser( new String( packet.getData() ) );
//MultiPrintStream.out.println("*********** Recived\n"+header);
String usn = header.getHTTPAttribute("USN");
String st = header.getHTTPAttribute("ST");
SSDPServiceInfo service;
// Get existing service
if( services_usn.containsKey( usn )){
service = services_usn.get( usn );
}
// Add new service
else{
service = new SSDPServiceInfo();
services_usn.put( usn, service);
if( !services_st.containsKey(st) )
services_st.put( st, new LinkedList<SSDPServiceInfo>() );
services_st.get( header.getHTTPAttribute("ST") ).add( service );
}
service.setLocation( header.getHTTPAttribute("LOCATION") );
service.setST( st );
service.setUSN( usn );
service.setExpirationTime(
System.currentTimeMillis() +
1000 * getCacheTime(header.getHTTPAttribute("Cache-Control")) );
//MultiPrintStream.out.println("*********** Recived\n"+service);
}
private long getCacheTime(String cache_control){
long ret = 0;
String[] tmp = cache_control.split(",");
for( String element : tmp ){
element = element.replaceAll("\\s", "").toLowerCase();
if( element.startsWith("max-age=") ){
ret = Long.parseLong( element.substring( "max-age=".length() ) );
}
}
return ret;
}
}

View file

@ -1,4 +1,4 @@
package zutil.network;
package zutil.network.ssdp;
import java.io.IOException;
import java.net.DatagramPacket;
@ -6,7 +6,6 @@ import java.net.InetAddress;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import zutil.MultiPrintStream;
import zutil.network.http.HTTPHeaderParser;
@ -49,25 +48,25 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
// instance specific values
private int cache_time;
private NotifyTimer notifyTimer = null;
/** HashMap that contains services as < SearchTargetName, Location > */
private HashMap<String, String> services;
/** A Map of all the used USN:s */
private HashMap<String, String> usn_map;
/** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */
private HashMap<String, SSDPServiceInfo> services;
public static void main(String[] args) throws IOException{
SSDPServer ssdp = new SSDPServer();
ssdp.addService("upnp:rootdevice", "nowhere");
SSDPServiceInfo service = new SSDPServiceInfo();
service.setLocation("nowhere");
service.setST("upnp:rootdevice");
ssdp.addService(service);
ssdp.start();
MultiPrintStream.out.println("SSDP Server running");
}
public SSDPServer() throws IOException{
super( null, SSDP_PORT, SSDP_MULTICAST_ADDR );
super( null, SSDP_MULTICAST_ADDR, SSDP_PORT );
super.setThread( this );
services = new HashMap<String, String>();
usn_map = new HashMap<String, String>();
services = new HashMap<String, SSDPServiceInfo>();
setChacheTime( DEFAULT_CACHE_TIME );
enableNotify( true );
@ -79,8 +78,8 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
* @param searchTarget is the ST value in SSDP
* @param location is the location of the service
*/
public void addService(String searchTarget, String location){
services.put( searchTarget, location );
public void addService(SSDPServiceInfo service){
services.put( service.getSearchTarget(), service );
}
/**
* Remove a service from being announced. This function will
@ -134,7 +133,6 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
*
* ***** REQUEST:
* M-SEARCH * HTTP/1.1
* S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6
* Host: 239.255.255.250:reservedSSDPport
* Man: "ssdp:discover"
* ST: ge:fridge
@ -142,12 +140,11 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
*
* ***** RESPONSE;
* HTTP/1.1 200 OK
* S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6
* Ext:
* Cache-Control: no-cache="Ext", max-age = 5000
* ST: ge:fridge
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
* AL: <blender:ixl><http://foo/bar>
* Location: http://localhost:80
*
*/
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
@ -155,7 +152,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
String msg = new String( packet.getData() );
HTTPHeaderParser header = new HTTPHeaderParser( msg );
MultiPrintStream.out.println(header);
//MultiPrintStream.out.println("**** Received:\n"+header);
// ******* Respond
// Check that the message is an ssdp discovery message
@ -172,13 +169,13 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
http.setStatusCode(200);
http.setHeader("Server", SERVER_INFO );
http.setHeader("ST", st );
http.setHeader("Location", services.get(st) );
http.setHeader("Location", services.get(st).getLocation() );
http.setHeader("EXT", "" );
http.setHeader("Cache-Control", "max-age = "+cache_time );
http.setHeader("USN", getUSN(st) );
http.setHeader("USN", services.get(st).getUSN() );
http.close();
MultiPrintStream.out.println("\n"+response);
//MultiPrintStream.out.println("********** Response:\n"+response);
byte[] data = response.toString().getBytes();
packet = new DatagramPacket(
data, data.length,
@ -225,7 +222,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
* NT: blenderassociation:blender
* NTS: ssdp:alive
* USN: someunique:idscheme3
* AL: <blender:ixl><http://foo/bar>
* Location: http://localhost:80
* Cache-Control: max-age = 7393
*/
public void sendNotify(String searchTarget){
@ -239,12 +236,12 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
http.setHeader("NT", searchTarget );
http.setHeader("NTS", "ssdp:alive" );
http.setHeader("Location", services.get(searchTarget) );
http.setHeader("Location", services.get(searchTarget).getLocation() );
http.setHeader("Cache-Control", "max-age = "+cache_time );
http.setHeader("USN", getUSN(searchTarget) );
http.setHeader("USN", services.get(searchTarget).getUSN() );
http.close();
MultiPrintStream.out.println("\n"+msg);
//MultiPrintStream.out.println("******** Notification:\n"+msg);
byte[] data = msg.toString().getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length,
@ -290,10 +287,10 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
http.setHeader("NT", searchTarget );
http.setHeader("NTS", "ssdp:byebye" );
http.setHeader("USN", getUSN(searchTarget) );
http.setHeader("USN", services.get(searchTarget).getUSN() );
http.close();
MultiPrintStream.out.println("\n"+msg);
//MultiPrintStream.out.println("******** ByeBye:\n"+msg);
byte[] data = msg.toString().getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length,
@ -305,19 +302,4 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
e.printStackTrace();
}
}
/**
* Generates an unique USN for the service
*
* @param searchTarget is the service ST name
* @return an unique string that corresponds to the service
*/
public String getUSN(String searchTarget){
if( !usn_map.containsKey( searchTarget ) ){
String usn = "uuid:" + UUID.nameUUIDFromBytes( (searchTarget+services.get(searchTarget)).getBytes() );
usn_map.put( searchTarget, usn );
return usn;
}
return usn_map.get( searchTarget );
}
}

View file

@ -0,0 +1,89 @@
package zutil.network.ssdp;
import java.util.Date;
import java.util.UUID;
/**
* This class contains information about a service from
* or through the SSDP protocol
*
* @author Ziver
*/
public class SSDPServiceInfo {
private String location;
private String st;
private String usn;
private long expiration_time;
/**
* @param l is the value to set the Location variable
*/
public void setLocation(String l) {
location = l;
}
/**
* @param st is the value to set the SearchTarget variable
*/
public void setST(String st) {
this.st = st;
}
/**
* @param usn is the value to set the USN variable
*/
protected void setUSN(String usn) {
this.usn = usn;
}
/**
* @param time sets the expiration time of values in this object
*/
protected void setExpirationTime(long time) {
expiration_time = time;
}
/**
* @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html"
*/
public String getLocation(){
return location;
}
/**
* @return the Search Target, e.g. "upnp:rootdevice"
*/
public String getSearchTarget(){
return st;
}
/**
* @return the expiration time for the values in this object
*/
public long getExpirationTime(){
return expiration_time;
}
/**
* @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 "
*/
public String getUSN(){
if( usn==null )
usn = genUSN();
return usn;
}
/**
* Generates an unique USN for the service
*
* @param searchTarget is the service ST name
* @return an unique string that corresponds to the service
*/
private String genUSN(){
return "uuid:" + UUID.nameUUIDFromBytes( (st+location).getBytes() ) +"::"+st;
}
public String toString(){
return "USN: "+usn+"\nLocation: "+location+"\nST: "+st+"\nExpiration-Time: "+new Date(expiration_time);
}
}

View file

@ -28,9 +28,22 @@ public class ThreadedUDPNetwork extends Thread{
protected DatagramSocket socket;
protected ThreadedUDPNetworkThread thread = null;
/**
* Creates a new unicast Clien instance of the class
*
* @param thread is the class that will handle incoming packets
* @throws SocketException
*/
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread) throws SocketException{
this.type = UDPType.UNICAST;
this.port = -1;
setThread( thread );
socket = new DatagramSocket();
}
/**
* Creates a new unicast instance of the sever
* Creates a new unicast Server instance of the class
*
* @param thread is the class that will handle incoming packets
* @param port is the port that the server should listen to
@ -45,14 +58,14 @@ public class ThreadedUDPNetwork extends Thread{
}
/**
* Creates a new multicast instance of the sever
* Creates a new multicast Server instance of the class
*
* @param thread is the class that will handle incoming packets
* @param port is the port that the server should listen to
* @param multicast_addr is the multicast address that the server will listen on
* @throws IOException
*/
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, int port, String multicast_addr ) throws IOException{
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, String multicast_addr, int port ) throws IOException{
this.type = UDPType.MULTICAST;
this.port = port;
setThread( thread );