Changed SSDPClient to use multicast and listen to the network

This commit is contained in:
Ziver Koc 2016-05-31 16:55:08 +02:00
parent 868364a0ca
commit 37c1809a61
7 changed files with 120 additions and 59 deletions

View file

@ -31,6 +31,7 @@ import java.util.Iterator;
public class HttpHeader {
// HTTP info
private boolean request;
private String type;
private String url;
private HashMap<String, String> urlAttributes;
@ -49,6 +50,18 @@ public class HttpHeader {
}
/**
* @return true if this header represents a server response
*/
public boolean isResponse(){
return !request;
}
/**
* @return true if this header represents a client request
*/
public boolean isRequest(){
return request;
}
/**
* @return the HTTP message type( ex. GET,POST...)
*/
@ -117,13 +130,7 @@ public class HttpHeader {
}
protected HashMap<String,String> getCookieMap(){
return cookies;
}
protected HashMap<String,String> getUrlAttributeMap(){
return urlAttributes;
}
protected void setIsRequest(boolean request) { this.request = request; }
protected void setRequestType(String type){
this.type = type.trim();
}
@ -146,6 +153,13 @@ public class HttpHeader {
headers.put(key.trim(), value.trim());
}
protected HashMap<String,String> getCookieMap(){
return cookies;
}
protected HashMap<String,String> getUrlAttributeMap(){
return urlAttributes;
}
public String toString(){

View file

@ -87,11 +87,13 @@ public class HttpHeaderParser {
protected static void parseStatusLine(HttpHeader header, String line){
// Server Response
if( line.startsWith("HTTP/") ){
header.setIsRequest(false);
header.setHTTPVersion( Float.parseFloat( line.substring( 5 , 8)));
header.setHTTPCode( Integer.parseInt( line.substring( 9, 12 )));
}
// Client Request
else if(line.contains("HTTP/")){
header.setIsRequest(true);
header.setRequestType( line.substring(0, line.indexOf(" ")));
header.setHTTPVersion( Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim()));
line = (line.substring(header.getRequestType().length()+1, line.lastIndexOf("HTTP/")));

View file

@ -208,9 +208,9 @@ public class HttpPrintStream extends OutputStream{
else{
if(res_status_code != null){
if( message_type==HttpMessageType.REQUEST )
out.print(req_type + " " + req_url + " HTTP/1.0");
out.print(req_type + " " + req_url + " HTTP/1.1");
else
out.print("HTTP/1.0 " + res_status_code + " " + getStatusString(res_status_code));
out.print("HTTP/1.1 " + res_status_code + " " + getStatusString(res_status_code));
out.println();
res_status_code = null;
req_type = null;

View file

@ -24,7 +24,6 @@
package zutil.net.ssdp;
import zutil.io.StringOutputStream;
import zutil.log.LogUtil;
import zutil.net.http.HttpHeader;
import zutil.net.http.HttpHeaderParser;
@ -32,6 +31,7 @@ import zutil.net.http.HttpPrintStream;
import zutil.net.threaded.ThreadedUDPNetwork;
import zutil.net.threaded.ThreadedUDPNetworkThread;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
@ -40,6 +40,9 @@ import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import static zutil.net.ssdp.SSDPServer.SSDP_MULTICAST_ADDR;
import static zutil.net.ssdp.SSDPServer.SSDP_PORT;
/**
* An SSDP client class that will request
* service information.
@ -48,6 +51,7 @@ import java.util.logging.Logger;
*/
public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
private static final Logger logger = LogUtil.getLogger();
public static final String USER_AGENT = "Zutil SSDP Client";
/** Mapping of search targets and list of associated services **/
private HashMap<String, LinkedList<StandardSSDPInfo>> services_st;
/** Map of all unique services received **/
@ -63,6 +67,7 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
* @throws IOException
*/
public SSDPClient() throws IOException{
super( SSDP_MULTICAST_ADDR, SSDP_PORT );
super.setThread(this);
services_st = new HashMap<>();
@ -88,14 +93,15 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
public void requestService(String searchTarget, HashMap<String,String> headers){
try {
// Generate an SSDP discover message
StringOutputStream msg = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
HttpPrintStream http = new HttpPrintStream( buffer, HttpPrintStream.HttpMessageType.REQUEST );
http.setRequestType("M-SEARCH");
http.setRequestURL("*");
http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR +":"+ SSDPServer.SSDP_PORT );
http.setHeader("Host", SSDP_MULTICAST_ADDR +":"+ SSDP_PORT );
http.setHeader("ST", searchTarget );
http.setHeader("Man", "\"ssdp:discover\"" );
http.setHeader("MX", "3" );
http.setHeader("USER-AGENT", USER_AGENT );
if(headers != null) {
for (String key : headers.keySet()) {
http.setHeader(key, headers.get(key));
@ -104,11 +110,12 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
logger.log(Level.FINEST, "Sending Multicast: "+ http);
http.flush();
byte[] data = msg.toString().getBytes();
byte[] data = buffer.toByteArray();
//System.out.println(new String(data)+"****************");
DatagramPacket packet = new DatagramPacket(
data, data.length,
InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ),
SSDPServer.SSDP_PORT );
InetAddress.getByName( SSDP_MULTICAST_ADDR ),
SSDP_PORT );
super.send( packet );
http.close();
} catch (Exception e) {
@ -193,38 +200,57 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
String msg = new String(packet.getData(), packet.getOffset(), packet.getLength());
HttpHeaderParser headerParser = new HttpHeaderParser(msg);
HttpHeader header = headerParser.read();
logger.log(Level.FINEST, "Received(from: " + packet.getAddress() + "): " + header);
String usn = header.getHeader("USN");
String st = header.getHeader("ST");
boolean newService = false;
StandardSSDPInfo service;
StandardSSDPInfo service = null;
// Get existing service
if (services_usn.containsKey(usn)) {
service = services_usn.get(usn);
}
// Add new service
// Remove service
if ("NOTIFY".equals(header.getRequestType()) && "ssdp:byebye".equalsIgnoreCase(header.getHeader("NTS"))){
logger.log(Level.FINER, "Received NOTIFY:byebye (from: " + packet.getAddress() + "): " + header);
if (service != null) {
services_usn.remove(usn);
if (services_st.containsKey(st))
services_st.get(st).remove(service);
if (listener != null)
listener.serviceLost(service);
}
}
// Existing or new service update
else if (header.isResponse() || "NOTIFY".equals(header.getRequestType())) {
logger.log(Level.FINER, "Received service update (from: " + packet.getAddress() + "): " + header);
boolean newService = false;
// Add new service
if (service == null){
newService = true;
service = new StandardSSDPInfo();
services_usn.put(usn, service);
if (!services_st.containsKey(st))
services_st.put(st, new LinkedList<StandardSSDPInfo>());
services_st.get(header.getHeader("ST")).add(service);
}
service.setLocation(header.getHeader("LOCATION"));
service.setST(st);
service.setUSN(usn);
service.setInetAddress(packet.getAddress());
if (header.getHeader("Cache-Control") != null) {
service.setExpirationTime(
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
}
service.readHeaders(header);
if (listener != null && newService)
listener.serviceDiscovered(service);
}
else {
newService = true;
service = new StandardSSDPInfo();
services_usn.put(usn, service);
if (!services_st.containsKey(st))
services_st.put(st, new LinkedList<StandardSSDPInfo>());
services_st.get(header.getHeader("ST")).add(service);
logger.log(Level.FINEST, "Ignored (from: " + packet.getAddress() + "): " + header);
}
service.setLocation(header.getHeader("LOCATION"));
service.setST(st);
service.setUSN(usn);
service.setInetAddress(packet.getAddress());
if (header.getHeader("Cache-Control") != null) {
service.setExpirationTime(
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
}
service.readHeaders(header);
if (listener != null && newService)
listener.newService(service);
} catch (IOException e){
logger.log(Level.SEVERE, null, e);
}
@ -243,6 +269,14 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
}
public interface SSDPServiceListener{
public void newService(StandardSSDPInfo service);
}
/**
* Is called when a new service is discovered. Will only be called once per service.
*/
void serviceDiscovered(StandardSSDPInfo service);
/**
* Is called when a service goes down and is not available anymore.
*/
void serviceLost(StandardSSDPInfo service);
}
}

View file

@ -33,6 +33,7 @@ import zutil.net.http.HttpPrintStream;
import zutil.net.threaded.ThreadedUDPNetwork;
import zutil.net.threaded.ThreadedUDPNetworkThread;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
@ -68,9 +69,8 @@ import java.util.logging.Logger;
*/
public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
private static final Logger logger = LogUtil.getLogger();
public static final String SERVER_INFO = "Zutil SSDP Java Server";
public static final String SERVER_INFO = "Zutil SSDP Server";
public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min
public static final int BUFFER_SIZE = 512;
public static final String SSDP_MULTICAST_ADDR = "239.255.255.250";
public static final int SSDP_PORT = 1900;
@ -186,8 +186,8 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
logger.log(Level.FINEST, "Received Multicast(from: "+packet.getAddress()+"): "+ header);
// Generate the SSDP response
StringOutputStream response = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( response );
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
HttpPrintStream http = new HttpPrintStream( buffer );
http.setStatusCode(200);
http.setHeader("Location", services.get(st).getLocation() );
http.setHeader("USN", services.get(st).getUSN() );
@ -200,8 +200,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
logger.log(Level.FINEST, "Sending Response: "+ http);
http.flush();
String strData = response.toString();
byte[] data = strData.getBytes();
byte[] data = buffer.toByteArray();
packet = new DatagramPacket(
data, data.length,
packet.getAddress(),
@ -255,8 +254,8 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
try {
SSDPServiceInfo service = services.get(searchTarget);
// Generate the SSDP response
StringOutputStream msg = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
HttpPrintStream http = new HttpPrintStream( buffer, HttpPrintStream.HttpMessageType.REQUEST );
http.setRequestType("NOTIFY");
http.setRequestURL("*");
http.setHeader("Server", SERVER_INFO );
@ -271,7 +270,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
logger.log(Level.FINEST, "Sending Notification: " + http);
http.flush();
byte[] data = msg.toString().getBytes();
byte[] data = buffer.toByteArray();
DatagramPacket packet = new DatagramPacket(
data, data.length,
InetAddress.getByName( SSDP_MULTICAST_ADDR ),
@ -309,8 +308,8 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
public void sendByeBye(String searchTarget){
try {
// Generate the SSDP response
StringOutputStream msg = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
HttpPrintStream http = new HttpPrintStream( buffer, HttpPrintStream.HttpMessageType.REQUEST );
http.setRequestType("NOTIFY");
http.setRequestURL("*");
http.setHeader("Server", SERVER_INFO );
@ -321,7 +320,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
logger.log(Level.FINEST, "Sending ByeBye: " + http);
http.flush();
byte[] data = msg.toString().getBytes();
byte[] data = buffer.toByteArray();
DatagramPacket packet = new DatagramPacket(
data, data.length,
InetAddress.getByName( SSDP_MULTICAST_ADDR ),

View file

@ -95,7 +95,8 @@ public class SOAPHttpPage implements HttpPage{
/**
* Enables session support, if enabled then a new instance
* of the SOAPInterface will be created, if disabled then
* only the given object will be used as an static interface
* only the given object will be used as an static interface.
* Default is false.
*
* @param enabled is if session should be enabled
*/

View file

@ -41,12 +41,23 @@ public class SSDPClientTest {
LogUtil.setGlobalLevel(Level.FINEST);
SSDPClient ssdp = new SSDPClient();
ssdp.requestService("upnp:rootdevice");
ssdp.requestService("urn:schemas-wifialliance-org:device:WFADevice:1");
ssdp.requestService("urn:dial-multiscreen-org:service:dial:1"); // Chromecast
ssdp.requestService("urn:schemas-upnp-org:device:InternetGatewayDevice:1"); // Routers
ssdp.start();
for(int i=0; true ;++i){
while( i==ssdp.getServicesCount("upnp:rootdevice") ){ try{Thread.sleep(100);}catch(Exception e){} }
System.out.println("************************" );
System.out.println("" + ssdp.getServices("upnp:rootdevice").get(i));
}
ssdp.setListener(new SSDPClient.SSDPServiceListener() {
@Override
public void serviceDiscovered(StandardSSDPInfo service) {
System.out.println("*********** DISCOVERY *************" );
System.out.println("" + service);
}
@Override
public void serviceLost(StandardSSDPInfo service) {
System.out.println("*********** LOST *************" );
System.out.println("" + service);
}
});
}
}