2016-05-14 73 views
0

我有一个用JavaFX编写的应用程序,用一个非常简单的界面来控制剧院中的一些灯光。基本上有两个按钮,一个可以在3秒内消失,另一个在3秒内消失。该应用程序连接到以太网到串行服务器(Sealevel Sealink 4104)来控制灯光。嵌入式Java Web服务器隐藏应用界面

我想添加一个浏览器界面,以便可以通过任何移动设备控制应用程序。根据我从此视频获得的代码,我添加了一个Java Web服务器。

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

的应用程序运行,我可以得到网页我要寻找一个在浏览器中。但是,我的应用程序界面从不出现。这个想法是,应用程序界面始终存在,以表明它正在运行。网页界面将可用于将控制选项扩展到移动设备。

此时的主要问题是如何让Web服务器在后台运行而不会干扰应用界面的功能?

Web服务器代码:

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); 
    } 
} 
} 

下面是接口代码:

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

如何提供网页上您的应用程序?想到JNLP,但这意味着整个应用程序将从服务器传输到客户端,在这种情况下,移动设备并不会成为一种好方法。 – hotzst

+0

用户打开浏览器并键入运行该应用程序的计算机的IP地址。例如,192.168.1.16:9000/www/light control.html。 – CelestialCoyote

回答

0

我要去猜测,JavaFX的需要它的线程回来。它调用start(),其中你称为webserver.StartServer(),这反过来仍然卡在一个无限的while(true)循环。你也应该在一个单独的线程中执行套接字接受循环(并根据需要正确关闭它)并让start方法返回。这就是说,我不会推荐尝试自己实现一个伪HTTP服务器 - 这只是额外的代码,工作和维护,并且如果它不符合RFC标准,可能会以各种方式破解。有很多可以使用的嵌入式轻量级HTTP服务器。作为JLHTTP的作者,我认为它可能与您的使用案例非常吻合,但还有其他许多可供选择的使用案例。

使用JLHTTP 2.1,你需要这样的事:

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(); 
} 

注:

  • 网站文件(HTML/JS/CSS/imags等)是由磁盘提供服务 - 该示例使用当前目录,但应将其更改为专用目录以防止意外访问敏感文件。
  • 只要它发送相应的操作参数和值,您的客户端代码就可以使用POST或GET请求,通过表单,AJAX,带查询参数的url等。
  • 您还应该正确关闭应用程序,连接,HTTPServer等。
  • 本示例接受单个开/关操作参数。如果您在客户端需要更多的灵活性,您可以传递各个命令/设备/值参数,并在上下文处理程序中创建light controller命令字符串。
  • 后,你得到的一切工作,你应该考虑安全性一样好,甚至在观众的一些孩子将开始与你的节目搞乱:-)
+0

我已将JLTHTTP服务器添加到我的应用程序并使其工作。 – CelestialCoyote