Added basic WebSocket functionality (no decoding yet)

This commit is contained in:
Luca Warmenhoven
2024-05-07 16:25:10 +02:00
parent 8993d8eed6
commit c5b08a4907
7 changed files with 184 additions and 8 deletions

View File

@@ -1,4 +0,0 @@
package com.example.fitbot.processing;
public class MotionProcessor {
}

View File

@@ -1,4 +1,4 @@
package com.example.fitbot.processing;
package com.example.fitbot.util.processing;
public class MotionData {

View File

@@ -1,4 +1,6 @@
package com.example.fitbot.processing;
package com.example.fitbot.util.processing;
import com.example.fitbot.util.server.WebSocket;
import java.io.InputStream;
@@ -17,9 +19,14 @@ public class MotionInputStream extends InputStream {
*/
public static MotionInputStream startListening()
{
// Create server
// Create socket server
WebSocket socket = WebSocket.createServer(80);
// Check if the socket
if ( socket != null )
{
socket.startListening();
}
return null;
}

View File

@@ -0,0 +1,4 @@
package com.example.fitbot.util.processing;
public class MotionProcessor {
}

View File

@@ -0,0 +1,15 @@
package com.example.fitbot.util.server;
import java.net.Socket;
/**
* Interface for handling WebSocket events.
*/
public interface IWebSocketHandler {
// Function for handling the connection of the WebSocket.
default void onConnected(Socket socket) {}
default void onDisconnected(Socket socket) {}
default void onMessageReceived(String message) {}
default void onError(Socket socket, String error) {}
}

View File

@@ -0,0 +1,66 @@
package com.example.fitbot.util.server;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class WebSocket {
private ServerSocket serverSocket;
private WebSocketConnectionHandler connectionHandler;
private final Set<Socket> clients = Collections.synchronizedSet(new HashSet<>());
protected IWebSocketHandler eventHandler = new IWebSocketHandler() {}; // NO-OP event handler.
/**
* Constructor for creating a new WebSocket server.
*/
private WebSocket() {}
/**
* Function for creating a new WebSocket server given the provided port.
* @param port The port to configure the server to.
* @return A WebSocket connection, or null if something went wrong.
*/
public static @Nullable WebSocket createServer(int port) {
try {
WebSocket socket = new WebSocket();
socket.serverSocket = new ServerSocket(port);
return socket;
} catch (IOException error)
{
String cause = error.getMessage() == null ? "Unknown reason" : error.getMessage();
Log.e("WebSocket -- Creating new WebSocket server", cause);
return null;
}
}
/**
* Method for listening for incoming connections.
*/
public void startListening() {
this.connectionHandler = new WebSocketConnectionHandler(this);
this.connectionHandler.listen();
}
/**
* Method for getting the ServerSocket connection
* @return The ServerSocket connection.
*/
public ServerSocket getSocket() {
return this.serverSocket;
}
/**
* Method for checking whether this WebSocket connection is connected.
* @return The connection status of the WebSocket.
*/
public boolean isConnected() {
return !this.serverSocket.isClosed();
}
}

View File

@@ -0,0 +1,88 @@
package com.example.fitbot.util.server;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WebSocketConnectionHandler implements Runnable {
private final WebSocket theSocket;
private Thread thread;
/**
* Constructor for WebSocketConnectionHandler.
* This class handles all new incoming Socket connections.
* @param webSocket The socket to check for new connections.
*/
protected WebSocketConnectionHandler(WebSocket webSocket) {
this.theSocket = webSocket;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void run() {
while (theSocket.isConnected()) {
try {
Socket newSocket = this.theSocket.getSocket().accept();
this.theSocket.eventHandler.onConnected(newSocket);
Scanner scanner = new Scanner(newSocket.getInputStream(), "UTF-8");
String data = scanner.useDelimiter("\\r\\n\\r\\n").next();
Matcher header = Pattern.compile("^GET").matcher(data);
// Check if the header contains the GET keyword
// If this is the case, upgrade the HTTP connection to WebSocket.
if (header.find()) {
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
match.find();
String SECAccept = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-1").digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(StandardCharsets.UTF_8)));
byte[] response = (
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
"Sec-WebSocket-Accept: " +
SECAccept + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
newSocket.getOutputStream().write(response, 0, response.length);
// TODO: Read data from socket and provide in onMessageReceived.
}
} catch (IOException | NoSuchAlgorithmException error) {
String reason = error.getMessage() == null ? "Unknown reason" : error.getMessage();
Log.e("WebSocketConnectionHandler", "Error listening to Socket connections: " + reason);
break;
}
}
}
/**
* Method for checking whether the connection handler is actively listening.
* @return Whether or not it's listening.
*/
public boolean isActive() {
return this.thread.isAlive();
}
/**
* Method for listening to all new incoming socket connections.
*/
public void listen() {
this.thread = new Thread(this);
this.thread.start();
Log.i("WebSocketConnectionHandler", "Listening started.");
}
}