commit
3fc7f6131a
@ -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/ |
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>net.lensfrex</groupId> |
||||
<artifactId>lesson</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<artifactId>exp1-oop</artifactId> |
||||
|
||||
<properties> |
||||
<maven.compiler.source>8</maven.compiler.source> |
||||
<maven.compiler.target>8</maven.compiler.target> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
</properties> |
||||
|
||||
</project> |
@ -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"); |
||||
} |
||||
} |
@ -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(); |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>net.lensfrex</groupId> |
||||
<artifactId>lesson</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<artifactId>exp2-thread</artifactId> |
||||
|
||||
<properties> |
||||
<maven.compiler.source>8</maven.compiler.source> |
||||
<maven.compiler.target>8</maven.compiler.target> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
</properties> |
||||
|
||||
</project> |
@ -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(); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>net.lensfrex</groupId> |
||||
<artifactId>lesson</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<artifactId>exp3-network</artifactId> |
||||
|
||||
<properties> |
||||
<maven.compiler.source>8</maven.compiler.source> |
||||
<maven.compiler.target>8</maven.compiler.target> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
</properties> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>commons-io</groupId> |
||||
<artifactId>commons-io</artifactId> |
||||
<version>2.11.0</version> |
||||
</dependency> |
||||
</dependencies> |
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-compiler-plugin</artifactId> |
||||
<configuration> |
||||
<source>1.8</source> |
||||
<target>1.8</target> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
</project> |
@ -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(); |
||||
} |
||||
} |
@ -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<Map.Entry<String, List<String>>> headers = urlConnection.getHeaderFields().entrySet(); |
||||
|
||||
for (Map.Entry<String, List<String>> entry : headers) { |
||||
String key = entry.getKey(); |
||||
List<String> 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(); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,4 @@ |
||||
package net.lensfrex.socket.client; |
||||
|
||||
public class Global { |
||||
} |
@ -0,0 +1,7 @@ |
||||
package net.lensfrex.socket.client; |
||||
|
||||
import javafx.scene.layout.Pane; |
||||
|
||||
public class MainUI extends Pane { |
||||
|
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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"; |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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) + |
||||
";}"; |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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<User> 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(); |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<groupId>net.lensfrex</groupId> |
||||
<artifactId>lesson</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
<packaging>pom</packaging> |
||||
<modules> |
||||
<module>exp1-oop</module> |
||||
<module>exp2-thread</module> |
||||
<module>exp3-network</module> |
||||
</modules> |
||||
|
||||
<properties> |
||||
<maven.compiler.source>8</maven.compiler.source> |
||||
<maven.compiler.target>8</maven.compiler.target> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
</properties> |
||||
|
||||
</project> |
@ -0,0 +1,7 @@ |
||||
package net.lensfrex; |
||||
|
||||
public class Main { |
||||
public static void main(String[] args) { |
||||
System.out.println("Hello world!"); |
||||
} |
||||
} |
Loading…
Reference in new issue