From 3fc7f6131adbef40ee2663bf3bf90d88c741c9a0 Mon Sep 17 00:00:00 2001 From: lensferno Date: Wed, 4 Jan 2023 11:53:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=88=E5=AD=98=E4=B8=AA=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 33 +++ exp1-oop/pom.xml | 20 ++ .../java/net/lensfrex/AbstractExtends.java | 30 +++ .../src/main/java/net/lensfrex/Interface.java | 26 +++ exp1-oop/src/main/java/net/lensfrex/Main.java | 51 +++++ .../src/main/java/net/lensfrex/Rectangle.java | 33 +++ exp2-thread/pom.xml | 20 ++ .../src/main/java/net/lensfrex/Main.java | 42 ++++ .../java/net/lensfrex/RabbitAndTortoise.java | 42 ++++ .../src/main/java/net/lensfrex/Ticket.java | 40 ++++ exp3-network/pom.xml | 39 ++++ .../src/main/java/net/lensfrex/Main.java | 53 +++++ .../net/lensfrex/URLConnectionResource.java | 44 ++++ .../net/lensfrex/socket/client/Client.java | 216 ++++++++++++++++++ .../net/lensfrex/socket/client/Global.java | 4 + .../net/lensfrex/socket/client/MainUI.java | 7 + .../client/controller/ClientConnection.java | 34 +++ .../net/lensfrex/socket/common/Common.java | 44 ++++ .../net/lensfrex/socket/common/Constant.java | 16 ++ .../net/lensfrex/socket/common/DataPack.java | 60 +++++ .../lensfrex/socket/common/MessageData.java | 80 +++++++ .../java/net/lensfrex/socket/common/User.java | 36 +++ .../java/net/lensfrex/socket/common/Util.java | 66 ++++++ .../net/lensfrex/socket/server/Server.java | 118 ++++++++++ pom.xml | 23 ++ src/main/java/net/lensfrex/Main.java | 7 + 26 files changed, 1184 insertions(+) create mode 100644 .gitignore create mode 100644 exp1-oop/pom.xml create mode 100644 exp1-oop/src/main/java/net/lensfrex/AbstractExtends.java create mode 100644 exp1-oop/src/main/java/net/lensfrex/Interface.java create mode 100644 exp1-oop/src/main/java/net/lensfrex/Main.java create mode 100644 exp1-oop/src/main/java/net/lensfrex/Rectangle.java create mode 100644 exp2-thread/pom.xml create mode 100644 exp2-thread/src/main/java/net/lensfrex/Main.java create mode 100644 exp2-thread/src/main/java/net/lensfrex/RabbitAndTortoise.java create mode 100644 exp2-thread/src/main/java/net/lensfrex/Ticket.java create mode 100644 exp3-network/pom.xml create mode 100644 exp3-network/src/main/java/net/lensfrex/Main.java create mode 100644 exp3-network/src/main/java/net/lensfrex/URLConnectionResource.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/client/Client.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/client/Global.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/client/MainUI.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/client/controller/ClientConnection.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/Common.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/Constant.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/DataPack.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/MessageData.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/User.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/common/Util.java create mode 100644 exp3-network/src/main/java/net/lensfrex/socket/server/Server.java create mode 100644 pom.xml create mode 100644 src/main/java/net/lensfrex/Main.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bc4bf7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/exp1-oop/pom.xml b/exp1-oop/pom.xml new file mode 100644 index 0000000..ddbc550 --- /dev/null +++ b/exp1-oop/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + net.lensfrex + lesson + 1.0-SNAPSHOT + + + exp1-oop + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/exp1-oop/src/main/java/net/lensfrex/AbstractExtends.java b/exp1-oop/src/main/java/net/lensfrex/AbstractExtends.java new file mode 100644 index 0000000..b69b5b3 --- /dev/null +++ b/exp1-oop/src/main/java/net/lensfrex/AbstractExtends.java @@ -0,0 +1,30 @@ +package net.lensfrex; + +public class AbstractExtends { + protected void run() { + Father father = new Child(); + + father.method1(); + father.method2(); + } +} + +// ------------------------------------------ +abstract class Father { + public abstract void method1(); + + public abstract void method2(); +} + +// ------------------------------------------- +class Child extends Father { + @Override + public void method1() { + System.out.println("Do some thing in method1"); + } + + @Override + public void method2() { + System.out.println("Do some thing in method2"); + } +} \ No newline at end of file diff --git a/exp1-oop/src/main/java/net/lensfrex/Interface.java b/exp1-oop/src/main/java/net/lensfrex/Interface.java new file mode 100644 index 0000000..c2a1a27 --- /dev/null +++ b/exp1-oop/src/main/java/net/lensfrex/Interface.java @@ -0,0 +1,26 @@ +package net.lensfrex; + +public class Interface { + protected void run() { + AnInterface anInterface = new AnInterface() { + @Override + public void method1() { + System.out.println("Do some thing in method1"); + } + + @Override + public void method2() { + System.out.println("Do some thing in method2"); + } + }; + + anInterface.method1(); + anInterface.method2(); + } +} + +interface AnInterface { + void method1(); + + void method2(); +} \ No newline at end of file diff --git a/exp1-oop/src/main/java/net/lensfrex/Main.java b/exp1-oop/src/main/java/net/lensfrex/Main.java new file mode 100644 index 0000000..d4887ef --- /dev/null +++ b/exp1-oop/src/main/java/net/lensfrex/Main.java @@ -0,0 +1,51 @@ +package net.lensfrex; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class Main { + public static void main(String[] args) throws Exception { + if (args.length > 0) { + execute(args[0]); + } else { + System.out.println("Witch to run? (1, 2, 3)"); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + execute(reader.readLine()); + } + } + + private static void execute(String option) { + Main main = new Main(); + + switch (option) { + case "1": + main.run1(); + break; + case "2": + main.run2(); + break; + case "3": + main.run3(); + break; + default: + System.out.printf("Invalid input for '%s'%n", option); + } + } + + private void run1() { + Rectangle rectangle = new Rectangle(3, 4); + + System.out.println("rectangleHeight: " + rectangle.getHeight()); + System.out.println("rectangleWidth: " + rectangle.getWidth()); + System.out.println("rectangleSize: " + rectangle.getSize()); + } + + private void run2() { + new Interface().run(); + } + + private void run3() { + new AbstractExtends().run(); + } +} \ No newline at end of file diff --git a/exp1-oop/src/main/java/net/lensfrex/Rectangle.java b/exp1-oop/src/main/java/net/lensfrex/Rectangle.java new file mode 100644 index 0000000..70f271b --- /dev/null +++ b/exp1-oop/src/main/java/net/lensfrex/Rectangle.java @@ -0,0 +1,33 @@ +package net.lensfrex; + +public class Rectangle { + private float height = 0; + private float width = 0; + + public Rectangle() {} + + public Rectangle(float height, float width) { + this.height = height; + this.width = width; + } + + public float getSize() { + return height * width; + } + + public float getHeight() { + return height; + } + + public void setHeight(float height) { + this.height = height; + } + + public float getWidth() { + return width; + } + + public void setWidth(float width) { + this.width = width; + } +} diff --git a/exp2-thread/pom.xml b/exp2-thread/pom.xml new file mode 100644 index 0000000..beedaf0 --- /dev/null +++ b/exp2-thread/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + net.lensfrex + lesson + 1.0-SNAPSHOT + + + exp2-thread + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/exp2-thread/src/main/java/net/lensfrex/Main.java b/exp2-thread/src/main/java/net/lensfrex/Main.java new file mode 100644 index 0000000..f9d0cd6 --- /dev/null +++ b/exp2-thread/src/main/java/net/lensfrex/Main.java @@ -0,0 +1,42 @@ +package net.lensfrex; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class Main { + public static void main(String[] args) throws Exception { + System.out.println("Thread Lesson report"); + + if (args.length > 0) { + execute(args[0]); + } else { + System.out.println("Witch to run? (1, 2)"); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + execute(reader.readLine()); + } + } + + private static void execute(String option) { + Main main = new Main(); + + switch (option) { + case "1": + main.run1(); + break; + case "2": + main.run2(); + break; + default: + System.out.printf("Invalid input for '%s'%n", option); + } + } + + private void run1() { + new RabbitAndTortoise().runTest(); + } + + private void run2() { + new Ticket().runTest(); + } +} \ No newline at end of file diff --git a/exp2-thread/src/main/java/net/lensfrex/RabbitAndTortoise.java b/exp2-thread/src/main/java/net/lensfrex/RabbitAndTortoise.java new file mode 100644 index 0000000..24b7a07 --- /dev/null +++ b/exp2-thread/src/main/java/net/lensfrex/RabbitAndTortoise.java @@ -0,0 +1,42 @@ +package net.lensfrex; + +public class RabbitAndTortoise { + public void runTest() { + Animal rabbit = new Animal("Rabbit", 16, 32); + Animal tortoise = new Animal("Tortoise", 8, 32); + + new Thread(rabbit).start(); + new Thread(tortoise).start(); + } +} + +class Animal implements Runnable { + private final String name; + + private final int speed; + private final int targetDistance; + + private int totalDistance = 0; + + public Animal(String name, int speed, int targetDistance) { + this.name = name; + this.speed = speed; + this.targetDistance = targetDistance; + } + + @Override + public void run() { + while (totalDistance <= targetDistance) { + try { + Thread.sleep((int) (Math.random() * 1000) + 500); + } catch (Exception ignored) { + return; + } + + totalDistance += speed; + System.out.printf("[%s] has run %d%n", name, totalDistance); + } + + System.out.printf("[%s] has finish the game.%n", name); + } +} diff --git a/exp2-thread/src/main/java/net/lensfrex/Ticket.java b/exp2-thread/src/main/java/net/lensfrex/Ticket.java new file mode 100644 index 0000000..ef6a7ce --- /dev/null +++ b/exp2-thread/src/main/java/net/lensfrex/Ticket.java @@ -0,0 +1,40 @@ +package net.lensfrex; + +public class Ticket { + public void runTest() { + TicketOffice ticketOffice = new TicketOffice(); + new Thread(ticketOffice, "0").start(); + new Thread(ticketOffice, "1").start(); + new Thread(ticketOffice, "2").start(); + } +} + +class TicketOffice implements Runnable { + private static int ticket = 8; + + @Override + public void run() { + String name = Thread.currentThread().getName(); + while(sellTicket(name)); + } + + private boolean sellTicket(String name) { + synchronized (this) { + if (ticket <= 0) { + System.out.printf("[%s]: Ticket was sold out.%n", name); + return false; + } + + System.out.printf("TicketOffice '%s' is selling ticket No.%s%n", name, ticket); + ticket--; + + try { + Thread.sleep(128); + } catch (Exception ignored) { + return false; + } + } + + return true; + } +} diff --git a/exp3-network/pom.xml b/exp3-network/pom.xml new file mode 100644 index 0000000..69a6ede --- /dev/null +++ b/exp3-network/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + net.lensfrex + lesson + 1.0-SNAPSHOT + + + exp3-network + + + 8 + 8 + UTF-8 + + + + commons-io + commons-io + 2.11.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/exp3-network/src/main/java/net/lensfrex/Main.java b/exp3-network/src/main/java/net/lensfrex/Main.java new file mode 100644 index 0000000..8d9a417 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/Main.java @@ -0,0 +1,53 @@ +package net.lensfrex; + +import net.lensfrex.socket.client.Client; +import net.lensfrex.socket.server.Server; +import net.lensfrex.socket.common.Util; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class Main { + public static void main(String[] args) throws Exception { + System.out.println("Thread Lesson report"); + + if (args.length > 0) { + execute(args[0]); + } else { + System.out.println("Witch to run? (1server, 1client, 2)"); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + execute(reader.readLine()); + } + } + + private static void execute(String option) { + Main main = new Main(); + + switch (option) { + case "1server": + main.run1Server(); + break; + case "1client": + main.run1Client(); + break; + case "2": + main.run2(); + break; + default: + System.out.printf("Invalid input for '%s'%n", option); + } + } + + private void run1Server() { + new Server().start(); + } + + private void run1Client() { + new Client().start(); + } + + private void run2() { + new URLConnectionResource().startTest(); + } +} \ No newline at end of file diff --git a/exp3-network/src/main/java/net/lensfrex/URLConnectionResource.java b/exp3-network/src/main/java/net/lensfrex/URLConnectionResource.java new file mode 100644 index 0000000..6c2874f --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/URLConnectionResource.java @@ -0,0 +1,44 @@ +package net.lensfrex; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class URLConnectionResource { + private static final String targetSite = "https://www.gnu.org/licenses/gpl-3.0.txt"; + public void startTest() { + URLConnection urlConnection; + try { + urlConnection = new URL(targetSite).openConnection(); + + Set>> headers = urlConnection.getHeaderFields().entrySet(); + + for (Map.Entry> entry : headers) { + String key = entry.getKey(); + List values = entry.getValue(); + for (String value : values) { + System.out.printf("%s: %s%n", key, value); + } + } + } catch (Exception e) { + e.printStackTrace(); + return; + } + + System.err.println("--------------------------------"); + + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8))) { + String line = bufferedReader.readLine(); + for (int i = 0; line != null && i < 10; i++, line = bufferedReader.readLine()) { + System.out.println(line); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/exp3-network/src/main/java/net/lensfrex/socket/client/Client.java b/exp3-network/src/main/java/net/lensfrex/socket/client/Client.java new file mode 100644 index 0000000..8b2ebf6 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/client/Client.java @@ -0,0 +1,216 @@ +package net.lensfrex.socket.client; + +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import javafx.scene.control.*; +import javafx.scene.layout.*; +import net.lensfrex.socket.client.controller.ClientConnection; +import net.lensfrex.socket.common.Common; +import net.lensfrex.socket.common.Constant; +import net.lensfrex.socket.common.DataPack; +import net.lensfrex.socket.common.MessageData; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Random; + +public class Client extends Application { + public void start() { + launch(); + } + + @Override + public void start(Stage stage) throws Exception { + Scene scene = new Scene(new UI().buildUI(), 515, 509); + + // 主题,不想要或者网络出错的话注释掉就好了 +// scene.getStylesheets().add(new URL("https://www.lensfrex.net/light.css").toExternalForm()); +// scene.getStylesheets().add(new URL("https://www.lensfrex.net/dark.css").toExternalForm()); + scene.getStylesheets().add(new URL("http://127.0.0.1/light.css").toExternalForm()); + + stage.setTitle("( ̄﹃ ̄)"); + stage.setScene(scene); + + stage.show(); + } +} + +class UI { + private static final String[] DEFAULT_USERNAMES = {"Rex", "Red", "Ash", "Aether"}; + + private Pane pane; + private VBox rootBox; + + private HBox serverControlVBox; + private TextField serverTextField; + + private TextArea messageTextArea; + private HBox userControlVBox; + private TextField usernameTextField; + private Button loginButton, logoutButton; + private HBox messageControlVBox; + private TextField messageTextField; + private Button sendButton; + + protected Parent buildUI() { + pane = new Pane(); + + rootBox = new VBox(); + rootBox.setPadding(new Insets(10, 10, 10, 10)); + rootBox.setSpacing(20); + rootBox.setPrefWidth(500); + rootBox.setPrefHeight(500); + + messageTextArea = new TextArea("暂无消息..."); + messageTextArea.setPrefHeight(700); + messageTextArea.setEditable(false); + + rootBox.getChildren().addAll(serverControlPane(), userControlPane(), messageTextArea, messageControlPane()); + pane.getChildren().addAll(rootBox); + + return pane; + } + + private HBox serverControlPane() { + serverControlVBox = new HBox(); + serverControlVBox.setSpacing(10); + serverControlVBox.setAlignment(Pos.CENTER_LEFT); + + Label serverLabel = new Label("服务器地址:"); + + serverTextField = new TextField("127.0.0.1:2333"); + serverTextField.setPrefWidth(320); + + serverControlVBox.getChildren().addAll(serverLabel, serverTextField); + + return serverControlVBox; + } + + + private HBox userControlPane() { + userControlVBox = new HBox(); + userControlVBox.setSpacing(10); + userControlVBox.setAlignment(Pos.CENTER_LEFT); + + Label usernameLabel = new Label("用户名:"); + + String username = DEFAULT_USERNAMES[new Random().nextInt(DEFAULT_USERNAMES.length)]; + usernameTextField = new TextField(username); + usernameTextField.setPrefWidth(200); + + loginButton = new Button("登录"); + loginButton.setOnAction(e -> { + try { + this.auth(Constant.OPCODE_LOGIN); + + logoutButton.setDisable(false); + loginButton.setDisable(true); + serverTextField.setDisable(true); + usernameTextField.setDisable(true); + messageControlVBox.setDisable(false); + } catch (Exception ex) { + ex.printStackTrace(); + messageTextArea.textProperty().set("登录服务器时发生错误:" + ex); + } + }); + + logoutButton = new Button("注销"); + logoutButton.setDisable(true); + logoutButton.setOnAction(e -> { + try { + this.auth(Constant.OPCODE_LOGOUT); + + logoutButton.setDisable(true); + loginButton.setDisable(false); + serverTextField.setDisable(false); + usernameTextField.setDisable(false); + messageControlVBox.setDisable(true); + } catch (Exception ex) { + ex.printStackTrace(); + messageTextArea.textProperty().set("登录服务器时发生错误:" + ex); + } + }); + + userControlVBox.getChildren().addAll(usernameLabel, usernameTextField, loginButton, logoutButton); + + return userControlVBox; + } + + private void auth(byte[] opcode) throws Exception { + String[] address = serverTextField.getText().split(":"); + int port = Integer.parseInt(address[1]); + + ClientConnection connection = new ClientConnection(address[0], port); + + byte[] data = usernameTextField.getText().getBytes(StandardCharsets.UTF_8); + OutputStream outputStream = connection.getOutputStream(); + + Common.send(data, opcode, outputStream); + connection.finishOutput(); + + InputStream inputStream = connection.getInputStream(); + byte[] header = new byte[4]; + inputStream.read(header); + + DataPack dataPack = Common.readDataPack(inputStream); + MessageData messageData = MessageData.parse(dataPack.getData()); + messageTextArea.textProperty().set(messageTextArea.getText() + + String.format("\n服务器[%s]响应:%s", messageData.getIdText(), messageData.getMessageText())); + connection.finishInput(); + + connection.disconnect(); + } + + private HBox messageControlPane() { + messageControlVBox = new HBox(); + messageControlVBox.setSpacing(10); + messageControlVBox.setAlignment(Pos.CENTER_LEFT); + messageControlVBox.setDisable(true); + + Label usernameLabel = new Label("输入消息:"); + + messageTextField = new TextField(); + messageTextField.setPrefWidth(320); + + sendButton = new Button("发送"); + sendButton.setOnAction(e -> { + String[] address = serverTextField.getText().split(":"); + int port = Integer.parseInt(address[1]); + try { + ClientConnection connection = new ClientConnection(address[0], port); + + MessageData messageData = new MessageData(usernameTextField.getText(), messageTextField.getText()); + + Common.send(messageData.toBytes(), Constant.OPCODE_SEND_MESSAGE, connection.getOutputStream()); + connection.finishOutput(); + + InputStream inputStream = connection.getInputStream(); + byte[] header = new byte[4]; + inputStream.read(header); + + DataPack dataPack = Common.readDataPack(inputStream); + MessageData responseData = MessageData.parse(dataPack.getData()); + messageTextArea.textProperty().set(messageTextArea.getText() + + String.format("\n服务器[%s]响应:%s", responseData.getIdText(), responseData.getMessageText())); + connection.finishInput(); + + connection.disconnect(); + } catch (IOException ex) { + ex.printStackTrace(); + messageTextArea.textProperty().set("登录服务器时发生错误:" + ex); + } + }); + + messageControlVBox.getChildren().addAll(usernameLabel, messageTextField, sendButton); + + return messageControlVBox; + } +} \ No newline at end of file diff --git a/exp3-network/src/main/java/net/lensfrex/socket/client/Global.java b/exp3-network/src/main/java/net/lensfrex/socket/client/Global.java new file mode 100644 index 0000000..4734616 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/client/Global.java @@ -0,0 +1,4 @@ +package net.lensfrex.socket.client; + +public class Global { +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/client/MainUI.java b/exp3-network/src/main/java/net/lensfrex/socket/client/MainUI.java new file mode 100644 index 0000000..71e4af7 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/client/MainUI.java @@ -0,0 +1,7 @@ +package net.lensfrex.socket.client; + +import javafx.scene.layout.Pane; + +public class MainUI extends Pane { + +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/client/controller/ClientConnection.java b/exp3-network/src/main/java/net/lensfrex/socket/client/controller/ClientConnection.java new file mode 100644 index 0000000..fe4e926 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/client/controller/ClientConnection.java @@ -0,0 +1,34 @@ +package net.lensfrex.socket.client.controller; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class ClientConnection { + private final Socket socket; + + public ClientConnection(String host, int port) throws IOException { + this.socket = new Socket(host, port); + } + + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + public void disconnect() throws IOException { + socket.close(); + } + + public void finishOutput() throws IOException { + socket.shutdownOutput(); + } + + public void finishInput() throws IOException { + socket.shutdownInput(); + } +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/Common.java b/exp3-network/src/main/java/net/lensfrex/socket/common/Common.java new file mode 100644 index 0000000..71fdb0e --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/Common.java @@ -0,0 +1,44 @@ +package net.lensfrex.socket.common; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class Common { + public static DataPack readDataPack(InputStream inputStream) { + return readDataPack(Util.readAllData(inputStream)); + } + + public static DataPack readDataPack(byte[] dataPackBytes) { + return DataPack.parse(dataPackBytes); + } + + public static int sendMessage(String id, String message, OutputStream outputStream) throws IOException { + MessageData messageData = new MessageData(id, message); + + return sendMessage(messageData, outputStream); + } + + public static int sendMessage(MessageData message, OutputStream outputStream) throws IOException { + return sendMessage(message.toBytes(), outputStream); + } + + public static int sendMessage(byte[] data, OutputStream outputStream) throws IOException { + return send(data, Constant.OPCODE_SEND_MESSAGE, outputStream); + } + + public static int send(byte[] data, byte[] opcode, OutputStream outputStream) throws IOException { + DataPack dataPack = new DataPack(opcode, data); + + return send(dataPack, outputStream); + } + + public static int send(DataPack dataPack, OutputStream outputStream) throws IOException { + outputStream.write(Constant.MAGIC_HEADER); + outputStream.write(dataPack.toBytes()); + + outputStream.flush(); + return dataPack.getDataLength(); + } +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/Constant.java b/exp3-network/src/main/java/net/lensfrex/socket/common/Constant.java new file mode 100644 index 0000000..ed1ebba --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/Constant.java @@ -0,0 +1,16 @@ +package net.lensfrex.socket.common; + +import java.nio.charset.StandardCharsets; + +public class Constant { + public static final int LENGTH_BYTE_SIZE = 4; + public static final byte[] MAGIC_HEADER = "AETH".getBytes(StandardCharsets.UTF_8); + + public static final int OPCODE_LENGTH = 4; + public static final byte[] OPCODE_REQUEST_MESSAGE = "RMSG".getBytes(StandardCharsets.UTF_8); + public static final byte[] OPCODE_SEND_MESSAGE = "SMSG".getBytes(StandardCharsets.UTF_8); + public static final byte[] OPCODE_LOGIN = "LOGI".getBytes(StandardCharsets.UTF_8); + public static final byte[] OPCODE_LOGOUT = "LOGO".getBytes(StandardCharsets.UTF_8); + + public static final String MESSAGE_OK = "OPERATION-OK"; +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/DataPack.java b/exp3-network/src/main/java/net/lensfrex/socket/common/DataPack.java new file mode 100644 index 0000000..cb0bcc5 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/DataPack.java @@ -0,0 +1,60 @@ +package net.lensfrex.socket.common; + +import java.io.ByteArrayOutputStream; +import java.io.Serializable; +import java.util.Arrays; + +public class DataPack implements Serializable { + private final byte[] opcode; + private final byte[] data; + + public DataPack(byte[] opcode, byte[] data) { + this.opcode = opcode; + this.data = data; + } + + public static DataPack parse(byte[] bytes) { + int offset = 0; + byte[] opcode = Arrays.copyOfRange(bytes, offset, offset + Constant.OPCODE_LENGTH); + + offset += Constant.OPCODE_LENGTH; + int dataLength = Util.bytesToInt32(Arrays.copyOfRange(bytes, offset, offset + Constant.LENGTH_BYTE_SIZE)); + + offset += Constant.LENGTH_BYTE_SIZE; + byte[] data = Arrays.copyOfRange(bytes, offset, offset + dataLength); + + return new DataPack(opcode, data); + } + + public byte[] toBytes() { + try { + int totalLength = Constant.OPCODE_LENGTH + Constant.LENGTH_BYTE_SIZE + data.length; + ByteArrayOutputStream stream = new ByteArrayOutputStream(totalLength); + + stream.write(opcode); + stream.write(Util.int32ToBytes(data.length)); + stream.write(data); + + return stream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public byte[] getOpcode() { + return opcode; + } + + public byte[] getData() { + return data; + } + + public int getTotalLength() { + return Constant.OPCODE_LENGTH + Constant.LENGTH_BYTE_SIZE + data.length; + } + + public int getDataLength() { + return data.length; + } +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/MessageData.java b/exp3-network/src/main/java/net/lensfrex/socket/common/MessageData.java new file mode 100644 index 0000000..ff66aca --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/MessageData.java @@ -0,0 +1,80 @@ +package net.lensfrex.socket.common; + +import java.io.ByteArrayOutputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public class MessageData implements Serializable { + private final byte[] id; + private final byte[] messageData; + + public MessageData(byte[] id, byte[] messageData) { + this.id = id; + this.messageData = messageData; + } + + public MessageData(String id, String messageData) { + this.id = id.getBytes(StandardCharsets.UTF_8); + this.messageData = messageData.getBytes(StandardCharsets.UTF_8); + } + + public static MessageData parse(byte[] data) { + int offset = 0; + int idLength = Util.bytesToInt32(Arrays.copyOfRange(data, offset, offset + Constant.LENGTH_BYTE_SIZE)); + + offset += Constant.LENGTH_BYTE_SIZE; + byte[] id = Arrays.copyOfRange(data, offset, offset + idLength); + + offset += idLength; + int messageDataLength = Util.bytesToInt32(Arrays.copyOfRange(data, offset, offset + Constant.LENGTH_BYTE_SIZE)); + + offset += Constant.LENGTH_BYTE_SIZE; + byte[] messageData = Arrays.copyOfRange(data, offset, offset + messageDataLength); + + return new MessageData(id, messageData); + } + + public byte[] toBytes() { + try { + int byteSize = Constant.LENGTH_BYTE_SIZE * 2 + id.length + messageData.length; + ByteArrayOutputStream stream = new ByteArrayOutputStream(byteSize); + + stream.write(Util.int32ToBytes(id.length)); + stream.write(id); + stream.write(Util.int32ToBytes(messageData.length)); + stream.write(messageData); + + return stream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public byte[] getId() { + return id; + } + + public String getIdText() { + return new String(id); + } + + public byte[] getMessageData() { + return messageData; + } + + public String getMessageText() { + return new String(messageData); + } + + @Override + public String toString() { + return "MessageData{" + + "idLength=" + id.length + + ", id='" + new String(id) + + "', messageLength=" + messageData.length + + ", messageData='" + new String(messageData) + + ";}"; + } +} \ No newline at end of file diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/User.java b/exp3-network/src/main/java/net/lensfrex/socket/common/User.java new file mode 100644 index 0000000..b9a797f --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/User.java @@ -0,0 +1,36 @@ +package net.lensfrex.socket.common; + +import java.util.Objects; + +public class User { + private final String id; + + public User(String id) { + this.id = id; + } + + public User(byte[] id) { + this.id = new String(id); + } + + public String getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return id.equals(user.id); + } + + public boolean equals(String id) { + return this.id.equals(id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/common/Util.java b/exp3-network/src/main/java/net/lensfrex/socket/common/Util.java new file mode 100644 index 0000000..17ffa14 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/common/Util.java @@ -0,0 +1,66 @@ +package net.lensfrex.socket.common; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +public class Util { + private static final char[] alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); + private static final Random random = new Random(); + + public static String randomString(int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(alphabet[random.nextInt(alphabet.length)]); + } + + return sb.toString(); + } + + public static int bytesToInt32(byte[] bytes) { + int result = 0; + for (int i = 0; i < 4; i++) { + int shift = (3 - i) * 8; + result += (bytes[i] & 0xFF) << shift; + } + + return result; + } + + public static byte[] int32ToBytes(int i) { + byte[] result = new byte[4]; + result[0] = (byte) ((i >> 24) & 0xFF); + result[1] = (byte) ((i >> 16) & 0xFF); + result[2] = (byte) ((i >> 8) & 0xFF); + result[3] = (byte) (i & 0xFF); + + return result; + } + + public static byte[] readAllData(InputStream inputStream) { + return readAllData(inputStream, 5);// read every 5kb in default + } + + public static byte[] readAllData(InputStream inputStream, int byteAllocation) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] bytes = new byte[1024 * byteAllocation]; + + while(true) { + int length = inputStream.read(bytes); + if (length == -1) { + break; + } + byteArrayOutputStream.write(bytes, 0, length); + } + + byteArrayOutputStream.flush(); + + return byteArrayOutputStream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/exp3-network/src/main/java/net/lensfrex/socket/server/Server.java b/exp3-network/src/main/java/net/lensfrex/socket/server/Server.java new file mode 100644 index 0000000..be11c07 --- /dev/null +++ b/exp3-network/src/main/java/net/lensfrex/socket/server/Server.java @@ -0,0 +1,118 @@ +package net.lensfrex.socket.server; + +import net.lensfrex.socket.common.*; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; +import java.util.HashSet; + +public class Server { + private static boolean stop = false; + + public void start() { + Server.stop = false; + + try (ServerSocket serverSocket = new ServerSocket(2333)) { + while (!stop) { + // Start a new thread to service more than one client + new ServerThread(serverSocket.accept()).start(); + } + } catch (Exception e) { + System.err.println("Some exception happened: " + e); + } + } + + public void stop() { + Server.stop = true; + } +} + +class ServerThread extends Thread { + private static final String SERVER_ID = "MASTER-SERVER"; + + private static final HashSet onlineUsers = new HashSet<>(); + + private final Socket socket; + + public ServerThread(Socket socket) { + this.socket = socket; + } + + @Override + public void run() { + try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) { + byte[] clientMagicHeader = new byte[Constant.MAGIC_HEADER.length]; + if (inputStream.read(clientMagicHeader) == -1) { + return; + } + + this.executeAction(inputStream, outputStream); + socket.shutdownOutput(); + socket.shutdownInput(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void executeAction(InputStream inputStream, OutputStream outputStream) throws IOException { + DataPack dataPack = Common.readDataPack(inputStream); + byte[] opcode = dataPack.getOpcode(); + + if (Arrays.equals(Constant.OPCODE_LOGIN, opcode)) { + this.login(new String(dataPack.getData())); + Common.sendMessage(SERVER_ID, Constant.MESSAGE_OK, outputStream); + } else if (Arrays.equals(Constant.OPCODE_LOGOUT, opcode)) { + this.logout(new String(dataPack.getData())); + Common.sendMessage(SERVER_ID, Constant.MESSAGE_OK, outputStream); + } else if (Arrays.equals(Constant.OPCODE_REQUEST_MESSAGE, opcode)) { + this.sendRandomMessage(outputStream); + } else if (Arrays.equals(Constant.OPCODE_SEND_MESSAGE, opcode)) { + String receivedMessage = this.receiveMessage(dataPack); + String responseMessage = String.format("Receive message '%s'", receivedMessage); + + Common.sendMessage(SERVER_ID, responseMessage, outputStream); + } + } + + private void login(String id) { + onlineUsers.add(new User(id)); + + System.out.printf("User '%s' login.%n", id); + } + + private void logout(String id) { + onlineUsers.remove(new User(id)); + + System.out.printf("User '%s' logout.%n", id); + } + + private void sendRandomMessage(OutputStream outputStream) throws IOException { + this.sendMessage(Util.randomString(64), outputStream); + } + + private void sendMessage(String messageText, OutputStream outputStream) throws IOException { + Common.sendMessage(SERVER_ID, messageText, outputStream); + } + + private String receiveMessage(DataPack dataPack) { + byte[] data = dataPack.getData(); + MessageData messageData = MessageData.parse(data); + + User user = new User(messageData.getIdText()); + + if (!onlineUsers.contains(user)) { + String message = "User " + user.getId() + " is not online, but send a message."; + System.err.println(message); + + return message; + } + + System.out.printf("Receive message '%s' from: '%s'%n", messageData.getMessageText(), user.getId()); + System.out.println("----------------"); + + return messageData.getMessageText(); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6d238f4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + net.lensfrex + lesson + 1.0-SNAPSHOT + pom + + exp1-oop + exp2-thread + exp3-network + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/src/main/java/net/lensfrex/Main.java b/src/main/java/net/lensfrex/Main.java new file mode 100644 index 0000000..2b24e07 --- /dev/null +++ b/src/main/java/net/lensfrex/Main.java @@ -0,0 +1,7 @@ +package net.lensfrex; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file