嵌入式Java Web服务器隐藏应用程序界面

时间:2016-05-14 16:50:57

标签: javafx webserver

我有一个用JavaFX编写的应用程序,用一个非常简单的界面来控制剧院中的一些灯光。基本上是两个按钮,一个用于淡化3秒以上的亮点,另一个用于淡化3秒钟。该应用程序连接到以太网到串行服务器(Sealevel Sealink 4104)以控制灯光。

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

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

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

此时的主要问题是如何让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);

}

}

1 个答案:

答案 0 :(得分:0)

我猜测JavaFX需要它的线程。它会调用start(),您可以在其中调用webserver.StartServer(),而while(true)仍会停留在无限start循环中。您也应该在单独的线程中执行套接字接受循环(并根据需要正确关闭它)并让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(); } 方法返回。

话虽这么说,我不建议您自己尝试实现伪HTTP服务器 - 这只是额外的代码,工作和维护,如果不符合RFC,可能会以各种方式中断。您可以使用大量可嵌入的轻量级HTTP服务器。作为JLHTTP的作者,我认为它可以很好地匹配您的用例,但还有许多其他选择。

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

# Specify Exception class for your context
class ValidationException < RuntimeError
end

def number_of_cars_from_input
  # Get user input
  print 'Enter an integer between 1 and 10: '
  number = gets.chomp.to_i
  # Validate input for your requirements
  unless (1..10).cover?(number)
    raise ValidationException, 'Interger entered is outside specified range.'
  end
  number
rescue ValidationException => err
  # Print exception and retry current method
  puts err
  retry
end

# Get car name from user input
def car_from_input
  print 'Enter name of a car model: '
  gets.chomp
end

# Create array with size equal to number from imput and fill it with cars
array_of_cars = Array.new(number_of_cars_from_input) { car_from_input }
# Separate cars in groups by 3 and join groups
puts array_of_cars.each_slice(3).map { |a| a.join(', ') }

注意:

  • 网站文件(html / js / css / imags等)是从磁盘提供的 - 该示例使用当前目录,但您应将其更改为专用目录,以防止无意中访问敏感文件。
  • 您的客户端代码可以使用POST或GET请求,通过表单,AJAX,带查询参数的url等,只要它发送相应的操作参数和值。
  • 您还应该正确关闭应用程序,连接,HTTPServer等。
  • 此示例接受单个开/关操作参数。如果您在客户端需要更多灵活性,则可以传递单个命令/设备/值参数,并在上下文处理程序中创建光控制器命令字符串。
  • 一切正常后,你应该考虑安全性,或者观众中的一些孩子会开始搞乱你的节目: - )