2016-05-14 10 views
0

Ich habe eine Anwendung in JavaFX geschrieben, um einige Lichter in einem Theater mit einer sehr einfachen Schnittstelle zu steuern. Im Grunde leuchten zwei Tasten, eine für das Überblenden, über 3 Sekunden und die andere für 3 Sekunden. Die App verbindet sich mit einem Ethernet-zu-Serial-Server (Sealevel Sealink 4104), um die Lichter zu steuern.eingebetteter Java Webserver verbirgt App-Schnittstelle

Ich möchte eine Browser-Schnittstelle hinzufügen, so dass die App über jedes mobile Gerät gesteuert werden kann. Ich habe einen Java-Webserver hinzugefügt, der auf dem Code basiert, den ich aus diesem Video erhalten habe.

https://www.youtube.com/watch?v=G4Z2PQfOHdY

Die App läuft, und ich kann die Web-Seite, die ich für im Browser suchen bekommen. Die Benutzeroberfläche meiner App wird jedoch nie angezeigt. Die Idee ist, dass die App-Schnittstelle immer vorhanden ist, um anzuzeigen, dass sie ausgeführt wird. Die Webseite-Schnittstelle wäre verfügbar, um die Steueroptionen auf ein mobiles Gerät zu erweitern.

Die Hauptfrage an diesem Punkt ist, wie bekomme ich den Webserver im Hintergrund laufen, ohne die Funktionsweise der App-Schnittstelle zu stören?

Der Web-Server-Code:

package lightcontrol2; 

import java.io.BufferedReader; 
import java.io.DataOutputStream; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.io.OutputStream; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.StringTokenizer; 


public final class JavaWebserver { 

public final void StartServer() throws Exception { 
    // Set port number. 
    int port = 9000; 

    // Establish the listening socket. 
    ServerSocket serverSocket = new ServerSocket(port); 

    // Process HTTP sevice requests in an infinite loop. 
    while (true) { 
     // Listen for TCP connection request. 
     Socket connectionSocket = serverSocket.accept(); 

     // Construct an object to process the HTTP request message. 
     HttpRequest request = new HttpRequest(connectionSocket); 

     // Create a new thread to process the request. 
     Thread thread = new Thread(request); 

     // Start the thread. 
     thread.start(); 
    } 
} 
} 

final class HttpRequest implements Runnable { 

// Return carriage return (CR) and line feed (LF). 
final static String CRLF = "\r\n"; 
Socket socket; 

// Constructor. 
public HttpRequest(Socket socket) throws Exception { 
    this.socket = socket; 
} 

// Implement the run() method of the Runnable interface. 
// Within run(), explicitly catch and handle exceptions 
// with try/ catch block. 

@Override 
public void run() { 
    try { 
     processRequest(); 
    } catch (Exception e){ 
     System.out.println(e); 
    } 
} 

private void processRequest() throws Exception { 
    // Get a reference to the socket's input and output streams. 
    InputStream instream = socket.getInputStream(); 
    DataOutputStream os = new DataOutputStream(socket.getOutputStream()); 

    // Set up input stream filters. 
    // Page 169, 10th line down or so . . . 
    // Reads the input data. 
    BufferedReader br = new BufferedReader(new InputStreamReader(instream)); 

    // Get the request line of the HTTP request message. 
    // Get path/file.html version of http 
    String requestLine = br.readLine(); 

    // Display the request line. 
    System.out.println(); 
    System.out.println(requestLine); 

    // Deal with the request. 
    // Extract the filename from the request line. 
    // This is an input method with deliminators. 
    StringTokenizer tokens = new StringTokenizer(requestLine); 

    // Skip over the method, which should be 'GET'. 
    tokens.nextToken(); 
    String fileName = tokens.nextToken(); 

    // Root of the server. 
    String root = "/www/"; 
    fileName = root + fileName; 

    // Open the requested file. 
    FileInputStream fis = null; 
    boolean fileExists = true; 

    try { 
     fis = new FileInputStream(fileName); 
    } catch (FileNotFoundException e) { 
     fileExists = false; 
    } 

    // Construct the response message. 
    String statusLine = null; 
    String contentTypeLine = null; 
    String entityBody = null; 

    if (fileExists) { 
     statusLine = "HTTP/1.0 200 OK" + CRLF; 
     contentTypeLine = "Content-type: " + contentType(fileName) + CRLF; 
    } 
    else { 
     statusLine = "HTTP/1.0 404 Not Found" + CRLF; 
     contentTypeLine = "Content-type: " + "text/html" + CRLF; 
     entityBody = "<HTML>" + 
       "<HEAD><TITLE>Not Found</TITLE></HEAD>" + 
       "<BODY>NOt Found</BODY></HTML>"; 
    } 

    //Send the status line. 
    os.writeBytes(statusLine); 

    // Sent the content type line. 
    os.writeBytes(contentTypeLine); 

    // Send a blank line to indicate the end of the header lines. 
    os.writeBytes(CRLF); 

    // Send the entity body. 
    if (fileExists) { 
     sendBytes(fis, os); 
     os.writeBytes(statusLine); 
     fis.close(); 
    } else { 
     os.writeBytes(statusLine); 
     os.writeBytes(entityBody); 
     os.writeBytes(contentTypeLine); 
    } 

    System.out.println("*****"); 
    System.out.println(fileName); 
    System.out.println("*****"); 

    // Get and display the header lines. 
    String headerLine = null; 
    while ((headerLine = br.readLine()).length() != 0) { 
     System.out.println(headerLine); 
    } 

    // Close streams and socket. 
    os.close(); 
    br.close(); 
    socket.close(); 
}  

private static String contentType(String fileName) { 
    if (fileName.endsWith(".htm") || fileName.endsWith(".html")) { 
     return "text/html"; 
    } 
    if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { 
     return "image/jpeg"; 
    } 
    if (fileName.endsWith(".gif")) { 
     return "image/gif"; 
    } 

    return "application/octet-stream"; 
} 

private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception { 
    // Construct 1K buffer to hold bytes on way to the socket. 
    byte[] buffer = new byte[1024]; 
    int bytes = 0; 

    // Copy requested file into the socket's output stream. 
    // read() returns -1, indicating end of file. 
    while ((bytes = fis.read(buffer)) != -1) { 
     os.write(buffer, 0, bytes); 
    } 
} 
} 

Hier ist die Interface-Code:

package lightcontrol2; 

import javafx.application.Application; 
import javafx.event.ActionEvent; 
import javafx.geometry.Insets; 
import javafx.geometry.Pos; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.GridPane; 
import javafx.stage.Stage; 


public class LightControl2 extends Application { 

@Override 
public void start(Stage primaryStage) throws Exception { 
    GridPane grid = createGrid(); 

    SealinkConnect connect = new SealinkConnect(); 
    JavaWebserver webserver = new JavaWebserver(); 

    Button btnOn = new Button(); 
    grid.add(btnOn, 0, 1); 
    btnOn.setText("3 Sec On");   
    btnOn.setOnAction((ActionEvent event) -> { 
     System.out.println("3N:100:A"); 
     connect.sendCommand("3N:100:A"); 
    }); 

    Button btnOff = new Button(); 
    grid.add(btnOff, 0, 2); 
    btnOff.setText("3 Sec Off"); 
    btnOff.setOnAction((ActionEvent event) -> { 
     System.out.println("3F:A"); 
     connect.sendCommand("3F:A"); 
    }); 

    BorderPane root = new BorderPane(); 
    root.setPadding(new Insets(10)); 
    root.setCenter(grid); 

    Scene scene = new Scene(root, 365, 300); 

    primaryStage.setTitle("Light Control Test"); 
    primaryStage.setScene(scene); 

    scene.getStylesheets().add 
     (LightControl2.class.getResource("style.css").toExternalForm()); 

    primaryStage.show(); 

    connect.socketConnect(); 
    webserver.StartServer(); 
} 

private GridPane createGrid() { 
    GridPane grid = new GridPane(); 
    grid.setAlignment(Pos.CENTER); 
    grid.setHgap(5); 
    grid.setVgap(10); 
    grid.setPadding(new Insets(10)); 
    return grid; 
} 

/** 
* @param args the command line arguments 
*/ 
public static void main(String[] args) { 
    launch(args); 

} 

} 
+0

Wie ist Ihre Anwendung auf der Webseite zur Verfügung gestellt? JNLP kommt mir in den Sinn, aber das würde bedeuten, dass die gesamte Anwendung vom Server auf den Client übertragen wird, das mobile in diesem Fall, was mir nicht als guter Ansatz erscheint. – hotzst

+0

Der Benutzer öffnet einen Browser und gibt die IP-Adresse des Computers ein, auf dem die App ausgeführt wird. Als Beispiel 192.168.1.16:9000/www/light control.html. – CelestialCoyote

Antwort

0

Ich werde erraten, dass JavaFX ihren Faden muss zurück. Er ruft start() auf, in dem Sie webserver.StartServer() aufrufen, das wiederum in einer unendlichen while(true) Schleife stecken bleibt. Sie sollten die Socket-Accepting-Schleife auch in einem separaten Thread ausführen (und ordnungsgemäß herunterfahren) und die Methode start zurückgeben lassen.

Ich würde nicht empfehlen zu versuchen, einen Pseudo-HTTP-Server auf eigene Faust zu implementieren - das ist nur zusätzlichen Code, Arbeit und Wartung und kann auf verschiedene Arten brechen, wenn es nicht RFC-konform ist. Es gibt viele einbettbare Lightweight-HTTP-Server, die Sie verwenden können. Als der Autor von JLHTTP, denke ich, es könnte eine gute Übereinstimmung für Ihren Anwendungsfall sein, aber es gibt viele andere zur Auswahl.

Mit JLHTTP 2.1, Sie so etwas wie dieses brauchen würde:

public void startWebServer() { 
    String dir = "."; // local folder to serve website files from 
    HTTPServer server = new HTTPServer(9000); // pick a port, any port 
    HTTPServer.VirtualHost host = server.getVirtualHost(null); // default virtual host 
    host.addContext("/", new FileContextHandler(new File(dir), "/")); // serve website files from disk directory 
    host.addContext("/api/lights", (request, response) -> { 
     Map<String, String> params = request.getParams(); 
     String action = params.get("action"); 
     if (action == null) 
      action = ""; 
     switch (action) { 
      case "on": 
       connect.sendCommand("3N:100:A"); 
       return 200; // ok 
      case "off": 
       connect.sendCommand("3F:A"); 
       return 200; // ok 
      default: 
       return 400; // bad request 
     } 
    }, "GET", "POST"); // support both GET and POST requests 
    server.start(); 
} 

Hinweise:

  • Die Website-Dateien (html/js/css/imags etc.) werden von der Festplatte serviert - In diesem Beispiel wird das aktuelle Verzeichnis verwendet. Sie sollten dies jedoch in ein dediziertes Verzeichnis ändern, um einen unbeabsichtigten Zugriff auf vertrauliche Dateien zu verhindern.
  • Ihr Clientcode kann entweder eine POST- oder GET-Anforderung über ein Formular, AJAX, URL mit Abfrageparametern usw. verwenden, solange der entsprechende Aktionsparameter und -wert gesendet wird.
  • Sie sollten auch die Anwendung, Verbindung, HTTPServer usw. ordnungsgemäß schließen.
  • Dieses Beispiel akzeptiert einen einzelnen Aktionsparameter on/off. Wenn Sie auf der Clientseite mehr Flexibilität benötigen, können Sie einzelne Befehls-/Geräte-/Wertparameter übergeben und die Light Controller-Befehlszeichenfolge im Kontexthandler erstellen.
  • Nachdem Sie alles bekommen arbeiten, sollten Sie die Sicherheit prüfen, wie gut oder ein Kind im Publikum wird mit der Show beginnen messing :-)
+0

Ich habe den JLTHTTP-Server zu meiner App hinzugefügt und funktioniert. – CelestialCoyote

Verwandte Themen