From 61c5b271de70761de1efc62e21c285fd42226ebf Mon Sep 17 00:00:00 2001 From: Thomas Johnson Date: Wed, 18 Dec 2019 16:39:50 -0500 Subject: [PATCH] Added most functionality to protocol drivers --- proto/ProtoHandler.java | 10 +++- proto/ProtoID.java | 7 +-- proto/ProtoIn.java | 67 ++++++++++++++++++++----- proto/ProtoOut.java | 105 ++++++++++++++++++++++++++++++++++++---- sample/Main.java | 105 +++++++++++++++++++++++++++++++++++----- 5 files changed, 252 insertions(+), 42 deletions(-) diff --git a/proto/ProtoHandler.java b/proto/ProtoHandler.java index 9745172..66cab1f 100644 --- a/proto/ProtoHandler.java +++ b/proto/ProtoHandler.java @@ -1,10 +1,16 @@ package proto; +import java.util.ArrayList; + public interface ProtoHandler { + void handleError(String username, byte msg[]); void handleJoin(String username); void handlePart(String username); - void handleListing(String username, String names[]); + void handleListing(String username, ArrayList names); void handleListingRequest(String username); - void handleUnknownCommand(byte id); + void handleMessage(String username, String message); + void handleFile(String username, String filename, byte data[]); + void handleUnknownCommand(int length, byte id, String username); + void handleClose(); } diff --git a/proto/ProtoID.java b/proto/ProtoID.java index d0fe2f0..32e39f2 100644 --- a/proto/ProtoID.java +++ b/proto/ProtoID.java @@ -13,15 +13,10 @@ public enum ProtoID FILE((byte) 5), INFO((byte) 6); - private byte b; + public byte b; ProtoID(byte b) { this.b = b; } - - public byte b() - { - return b; - } } diff --git a/proto/ProtoIn.java b/proto/ProtoIn.java index a2d133f..60ebef6 100644 --- a/proto/ProtoIn.java +++ b/proto/ProtoIn.java @@ -1,8 +1,10 @@ package proto; import java.io.DataInputStream; +import java.io.EOFException; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.stream.IntStream; public class ProtoIn { @@ -10,6 +12,11 @@ public class ProtoIn public boolean loop; private ProtoHandler handler; + public void init() + { + this.loop = true; + new Thread(() -> mainLoop()).start(); + } public ProtoIn(DataInputStream dis, ProtoHandler handler) { @@ -17,25 +24,38 @@ public class ProtoIn this.handler = handler; } - private String readname() throws java.io.IOException + private String readName() throws java.io.IOException { - byte length = dis.readByte(); - byte[] bytes = {}; + int length = Byte.toUnsignedInt(dis.readByte()); + byte bytes[] = new byte[length]; dis.read(bytes, 0, length); return new String(bytes, Charset.forName("UTF-8")); } - void mainLoop() + private void mainLoop() { while (this.loop) { try { byte id = dis.readByte(); int length = dis.readInt(); + String username = readName(); switch (id) { + case 0: + byte msg[] = new byte[length]; + dis.read(msg, 0, length); + new Thread(() -> handler.handleError(username, msg)); + break; + case 1: + IntStream.range(0, length).forEach((x) -> { try { dis.read(); } catch (Exception e) {} } ); + new Thread(() -> handler.handleJoin(username)).start(); + break; + case 2: + IntStream.range(0, length).forEach((x) -> { try { dis.read(); } catch (Exception e) {} } ); + new Thread(() -> handler.handlePart(username)).start(); + break; case 3: - String username = readname(); if (length == 0) { Thread t = new Thread(() -> handler.handleListingRequest(username)); @@ -46,18 +66,43 @@ public class ProtoIn int a = 0; while (a < length) { - String name = readname(); - a += name.length(); - names.add(name); + int nLength = Byte.toUnsignedInt(dis.readByte()); + byte bytes[] = new byte[nLength]; + dis.read(bytes, 0, nLength); + a += nLength; + names.add(new String(bytes, "UTF-8")); } - handler.handleListing(username, (String[]) names.toArray()); + handler.handleListing(username, names); } break; + case 4: + byte[] msgb = new byte[length]; + dis.read(msgb, 0, length); + String message = new String(msgb, "UTF-8"); + new Thread(() -> handler.handleMessage(username, message)).start(); + break; + case 5: + int fnlength = Byte.toUnsignedInt(dis.readByte()); + byte fnameb[] = new byte[fnlength]; + dis.read(fnameb, 0, fnlength); + String fname = new String(fnameb, "UTF-8"); + int flen = length - fnlength - 1; + byte data[] = new byte[flen]; + dis.read(data, 0, flen); + new Thread(() -> handler.handleFile(username, fname, data)).start(); + break; + case 6: + // TODO + break; default: - Thread t = new Thread(() -> handler.handleUnknownCommand(id)); + Thread t = new Thread(() -> handler.handleUnknownCommand(length, id, username)); t.start(); } - } catch (Exception e) {} // Not much we can do about this + } catch (EOFException e) + { + this.loop = false; + handler.handleClose(); + } catch (Exception e) { e.printStackTrace(); } // Not much we can do about this } } } diff --git a/proto/ProtoOut.java b/proto/ProtoOut.java index d9a0738..7d2936d 100644 --- a/proto/ProtoOut.java +++ b/proto/ProtoOut.java @@ -1,25 +1,110 @@ package proto; -import java.io.DataOutputStream; -import java.io.IOException; +import javafx.scene.chart.XYChart; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.stream.Collector; +import java.util.stream.Collectors; public class ProtoOut { private DataOutputStream out; + private DataOutputStream real_out; + private ByteArrayOutputStream buffer; public ProtoOut(DataOutputStream out) { - this.out = out; + this.real_out = out; + this.buffer = new ByteArrayOutputStream(); + this.out = new DataOutputStream(buffer); } - public void requestUserList(String username) throws IOException + private void commit() throws java.io.IOException + { + real_out.write(buffer.toByteArray()); + real_out.flush(); + buffer = new ByteArrayOutputStream(); + this.out = new DataOutputStream(buffer); + } + + private void checkName(String name) throws IOException { - if (username.length() > 127) + if (name.length() > 255) throw new IOException("Username too long"); - out.writeByte(3); - out.writeShort(0); - out.writeByte((byte) username.length()); - out.writeUTF(username); - out.flush(); + } + + private void writeUserName(String name) throws IOException + { + checkName(name); + byte nameBytes[] = name.getBytes("UTF-8"); + out.writeByte(nameBytes.length); + out.write(nameBytes); + } + + public void sendJoin(String username) throws IOException + { + out.writeByte(ProtoID.JOIN.b); + out.writeInt(0); + writeUserName(username); + commit(); + } + + public void sendPart(String username) throws IOException + { + out.writeByte(ProtoID.PART.b); + out.writeInt(0); + writeUserName(username); + commit(); + } + + public void requestUserList(String username) throws IOException + { + out.writeByte(ProtoID.LISTING.b); // type: listing + out.writeInt(0); // length + writeUserName(username); // username + commit(); // Make it so! + } + + public void giveUserListing(String username, String names[]) throws IOException + { + out.writeByte(ProtoID.LISTING.b); + // I can recognize bad code when I write it, and this is bad code. + out.writeInt(Arrays.asList(names).stream().collect(Collectors.summingInt((s) -> s.getBytes().length))); + writeUserName(username); + for (String n : names) + { + writeUserName(n); + } + commit(); + } + + public void sendMessage(String username, String message) throws IOException + { + out.writeByte(ProtoID.MESSAGE.b); + byte msgb[] = message.getBytes("UTF-8"); + out.writeInt(msgb.length); + writeUserName(username); + out.write(msgb); + } + + public void sendFile(String username, String filename, byte data[]) throws IOException + { + out.writeByte(ProtoID.FILE.b); + byte fnb[] = filename.getBytes("UTF-8"); + if (fnb.length > 255) + throw new IOException("File name too long"); + out.writeInt(fnb.length + data.length + 1); + writeUserName(username); + out.writeByte(fnb.length); + out.write(fnb); + out.write(data); + } + + public void sendInfo(String username) throws IOException + { + // TODO + throw new IOException("not implemented"); } } diff --git a/sample/Main.java b/sample/Main.java index 5333744..0a0148f 100644 --- a/sample/Main.java +++ b/sample/Main.java @@ -1,23 +1,102 @@ package sample; -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; +import proto.ProtoHandler; +import proto.ProtoIn; +import proto.ProtoOut; -public class Main extends Application { +import java.awt.desktop.OpenURIEvent; +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; + +public class Main +{ + public static void main(String[] args) throws IOException { + ServerSocket ss = new ServerSocket(25519); + Socket o = new Socket("127.0.0.1", 25519); + Socket i = ss.accept(); + OutputStream os = o.getOutputStream(); + InputStream is = i.getInputStream(); + ProtoOut po = new ProtoOut(new DataOutputStream(os)); + ProtoIn pi = new ProtoIn(new DataInputStream(is), new TestHandler()); + pi.init(); + po.requestUserList("me"); + po.sendJoin("ji"); + po.sendMessage("ji", "yeet me off Snell"); + po.sendFile("ji", "yeet.jpg", new byte[]{1,2,3,4,5}); + po.sendPart("ji"); + po.requestUserList("aaaa"); + o.close(); + + while (true) + { +// System.out.println(is.read()); + } + } +} + +class TestHandler implements ProtoHandler +{ + public TestHandler() {} + + @Override + synchronized public void handleError(String username, byte[] msg) + { + System.out.println("error"); + } + + @Override + synchronized public void handleJoin(String username) + { + System.out.println("joined: " + username); + } + + @Override + synchronized public void handlePart(String username) + { + System.out.println("parted: " + username); + } @Override - public void start(Stage primaryStage) throws Exception{ - Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); - primaryStage.setTitle("Hello World"); - primaryStage.setScene(new Scene(root, 300, 275)); - primaryStage.show(); + synchronized public void handleListing(String username, ArrayList names) + { + System.out.println("listing for " + username); + for (String n : names) + { + System.out.println(n); + } + System.out.println("end listing"); } + @Override + synchronized public void handleListingRequest(String username) + { + System.out.println("listing request from " + username); + } - public static void main(String[] args) { - launch(args); + @Override + synchronized public void handleMessage(String username, String message) + { + System.out.println(username + ": " + message); + } + + @Override + synchronized public void handleFile(String username, String filename, byte data[]) + { + System.out.println(username + " sent a file called " + filename); + } + + @Override + synchronized public void handleUnknownCommand(int length, byte id, String username) + { + System.out.print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHH!!!!!!!!!!!!!!!!!!!!! "); + System.out.println("(" + id + ")"); + } + + @Override + synchronized public void handleClose() + { + System.out.println("closed"); } }