diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4a601a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+### IntelliJ IDEA ###
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+.idea
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/client/client.iml b/client/client.iml
new file mode 100644
index 0000000..99e828d
--- /dev/null
+++ b/client/client.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/com/soroko/client/Client.java b/client/src/com/soroko/client/Client.java
index 8dd8583..e37e045 100644
--- a/client/src/com/soroko/client/Client.java
+++ b/client/src/com/soroko/client/Client.java
@@ -1,7 +1,8 @@
package com.soroko.client;
-import com.soroko.common.common.SendReceive;
-import com.soroko.common.common.Message;
+import com.soroko.common.FileMessage;
+import com.soroko.common.SendReceive;
+import com.soroko.common.Message;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -12,40 +13,124 @@ public class Client {
private InetSocketAddress address;
private String username;
private Scanner scanner;
+ private SendReceive connectionHandler;
+ private boolean FilesAreEmpty;
public Client(InetSocketAddress address) {
this.address = address;
scanner = new Scanner(System.in);
}
+ public void saveCommand() throws InterruptedException {
+ String filepath = "";
+ String description = "";
+ if (!FilesAreEmpty) {
+ System.out.println("Укажите папку, в которую необходимо загрузить файл из сервера");
+ filepath = scanner.nextLine();
+ System.out.println("Введите название файла из списка доступных файлов:");
+ description = scanner.nextLine();
+ }
+ FileMessage fileMessage = new FileMessage(description, filepath);
+ fileMessage.setFilePath(filepath);
+ try {
+ connectionHandler.sendFileDescription(fileMessage);
+ } catch (IOException e) {
+ connectionHandler.close();
+ }
+ }
+ public void loadCommand() {
+ System.out.println("Введите путь, по которому необходимо загрузить файл на сервер");
+ String filepath = scanner.nextLine();
+ System.out.println("Введите описание файла:");
+ String description = scanner.nextLine();
+ System.out.println("Введите размер файла в мегабайтах:");
+ int size = scanner.nextInt();
+ FileMessage fileMessage = new FileMessage(description, size);
+ fileMessage.setFilePath(filepath);
+ try {
+ connectionHandler.sendFileDescription(fileMessage);
+ } catch (IOException e) {
+ connectionHandler.close();
+ }
+ }
- public void startClient() {
- System.out.println("Введите имя");
- username = scanner.nextLine();
- while (true) {
- System.out.println("Введите текст сообщения");
- String text = scanner.nextLine();
- if (text.equals("/exit")) break;
- try (SendReceive connectionHandler
- = new SendReceive(new Socket(
- address.getHostName(),
- address.getPort()
- ))) {
+ private class Writer extends Thread {
+ public void run() {
+ boolean isLoadCommand = false;
+ boolean isSaveCommand = false;
+ while (true) {
+ if (isLoadCommand) {
+ loadCommand();
+ } else if (isSaveCommand) {
+ try {
+ saveCommand();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ } else System.out.println("Введите текст сообщения");
+ isLoadCommand = false;
+ isSaveCommand = false;
+ String text = scanner.nextLine();
+ if (text.equalsIgnoreCase("/loadfile")) isLoadCommand = true;
+ if (text.equalsIgnoreCase("/savefile")) isSaveCommand = true;
+ if (text.equalsIgnoreCase("/exit")) {
+ System.out.println("Соединение прекращено");
+ connectionHandler.close();
+ break;
+ }
Message message = new Message(username);
message.setText(text);
try {
connectionHandler.send(message);
- Message fromServer = connectionHandler.receive();
- System.out.println(fromServer.getText());
- } catch (IOException e) {
+ } catch (IOException ignored) {
+ connectionHandler.close();
}
+ }
+ }
+ }
- } catch (Exception e) {
+ private class Reader extends Thread {
+ public void run() {
+ while (true) {
+ Message message;
+ try {
+ message = connectionHandler.receive();
+ FilesAreEmpty = message.getFilesAreEmpty();
+ if (message.getText().equalsIgnoreCase("/exit")) {
+ connectionHandler.close();
+ break;
+ }
+ } catch (IOException ignored) {
+ connectionHandler.close();
+ break;
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println(message.getText());
}
}
}
+
+ public void createConnection() throws IOException {
+ connectionHandler = new SendReceive(
+ new Socket(address.getHostName(), address.getPort()));
+ }
+
+ public void startClient() {
+ System.out.println("Введите имя");
+ username = scanner.nextLine();
+ try {
+ createConnection();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ new Writer().start();
+ new Reader().start();
+ }
}
+
+
diff --git a/common/common.iml b/common/common.iml
new file mode 100644
index 0000000..c90834f
--- /dev/null
+++ b/common/common.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/com/soroko/common/FileMessage.java b/common/src/com/soroko/common/FileMessage.java
new file mode 100644
index 0000000..7c5fda1
--- /dev/null
+++ b/common/src/com/soroko/common/FileMessage.java
@@ -0,0 +1,45 @@
+package com.soroko.common;
+
+import java.io.Serializable;
+import java.nio.file.Paths;
+
+public class FileMessage implements Serializable {
+ private String description;
+ private int size;
+ private String filePath;
+
+
+ public FileMessage(String description, String filepath) {
+ this.description = description;
+ this.filePath = filepath;
+ }
+
+ public FileMessage(String description, int size) {
+ this.description = description;
+ this.size = size;
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public String toString() {
+ String fileName = Paths.get(filePath).getFileName().toString();
+ return "\n" + "название: " + fileName +
+ ", описание: " + description +
+ ", размер в мб: " + size;
+ }
+}
diff --git a/common/src/com/soroko/common/common/Message.java b/common/src/com/soroko/common/Message.java
similarity index 51%
rename from common/src/com/soroko/common/common/Message.java
rename to common/src/com/soroko/common/Message.java
index 7dde2df..b18f7ce 100644
--- a/common/src/com/soroko/common/common/Message.java
+++ b/common/src/com/soroko/common/Message.java
@@ -1,12 +1,12 @@
-package com.soroko.common.common;
+package com.soroko.common;
import java.io.Serializable;
-import java.time.LocalDateTime;
public class Message implements Serializable {
- String sender;
- String text;
- LocalDateTime sentAt;
+ private String sender;
+ private String text;
+ private String sentAt;
+ private boolean FilesAreEmpty;
public Message(String sender) {
this.sender = sender;
@@ -16,10 +16,6 @@ public String getSender() {
return sender;
}
- public void setSender(String sender) {
- this.sender = sender;
- }
-
public String getText() {
return text;
}
@@ -28,11 +24,19 @@ public void setText(String text) {
this.text = text;
}
- public LocalDateTime getSentAt() {
+ public String getSentAt() {
return sentAt;
}
- public void setSentAt(LocalDateTime sentAt) {
+ public void setSentAt(String sentAt) {
this.sentAt = sentAt;
}
+
+ public boolean getFilesAreEmpty() {
+ return FilesAreEmpty;
+ }
+
+ public void setFilesAreEmpty(boolean filesAreEmpty) {
+ FilesAreEmpty = filesAreEmpty;
+ }
}
diff --git a/common/src/com/soroko/common/SendReceive.java b/common/src/com/soroko/common/SendReceive.java
new file mode 100644
index 0000000..b522159
--- /dev/null
+++ b/common/src/com/soroko/common/SendReceive.java
@@ -0,0 +1,63 @@
+package com.soroko.common;
+
+import java.io.*;
+import java.net.Socket;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+public class SendReceive implements AutoCloseable {
+ private ObjectOutputStream outputStream;
+ private ObjectInputStream inputStream;
+ private Socket socket;
+
+ public SendReceive(Socket socket) throws IOException {
+ this.socket = Objects.requireNonNull(socket);
+ outputStream = new ObjectOutputStream(socket.getOutputStream());
+ inputStream = new ObjectInputStream(socket.getInputStream());
+ }
+
+ public void send(Message message) throws IOException {
+ DateTimeFormatter formatTime = DateTimeFormatter.ofPattern("HH:mm:ss");
+ LocalTime localTime = LocalTime.now();
+ message.setSentAt(localTime.format(formatTime));
+ outputStream.writeObject(message);
+ outputStream.flush();
+ }
+
+ public void sendFileDescription(FileMessage fileMessage) throws IOException {
+ outputStream.writeObject(fileMessage);
+ outputStream.flush();
+ }
+
+ public Message receive() throws IOException, ClassNotFoundException {
+ try {
+ return (Message) inputStream.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public FileMessage receiveFileDescription() throws IOException, ClassNotFoundException {
+ try {
+ return (FileMessage) inputStream.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ if (!socket.isClosed()) {
+ inputStream.close();
+ outputStream.close();
+ socket.close();
+ }
+ } catch (IOException ignored) {
+ System.out.println("Проблема при закрытии потоков");
+ }
+
+ }
+}
diff --git a/common/src/com/soroko/common/WriteMessage.java b/common/src/com/soroko/common/WriteMessage.java
new file mode 100644
index 0000000..faa31e6
--- /dev/null
+++ b/common/src/com/soroko/common/WriteMessage.java
@@ -0,0 +1,41 @@
+package com.soroko.common;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+public class WriteMessage extends Thread {
+ private SendReceive connectionHandler;
+ private String username;
+ private Scanner scanner;
+ private int counter;
+
+ public WriteMessage(SendReceive sendReceive, String username, Scanner scanner) {
+ this.connectionHandler = sendReceive;
+ this.username = username;
+ this.scanner = scanner;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("Введите имя");
+ username = scanner.nextLine();
+
+ while (true) {
+ System.out.println("Введите текст сообщения");
+ String text = scanner.nextLine();
+ if (text.equals("/exit")) break;
+ Message message = new Message(username);
+ message.setText(text);
+ try {
+ Message fromServer;
+ try {
+ fromServer = connectionHandler.receive();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println(fromServer.getText());
+ } catch (IOException e) {
+ }
+ }
+ }
+}
diff --git a/common/src/com/soroko/common/common/SendReceive.java b/common/src/com/soroko/common/common/SendReceive.java
deleted file mode 100644
index 9de2152..0000000
--- a/common/src/com/soroko/common/common/SendReceive.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.soroko.common.common;
-
-import java.io.*;
-import java.net.Socket;
-import java.time.Instant;
-import java.time.LocalDateTime;
-
-public class SendReceive implements AutoCloseable {
- private ObjectOutputStream outputStream;
- private ObjectInputStream inputStream;
- private Socket socket;
-
-
-
- public SendReceive(Socket socket) throws IOException {
- outputStream = new ObjectOutputStream(socket.getOutputStream());
- inputStream = new ObjectInputStream(socket.getInputStream());
- }
-
- public void send(Message message) throws IOException {
- message.setSentAt(LocalDateTime.now());
- outputStream.writeObject(message);
- outputStream.flush();
- }
-
- public Message receive() throws IOException, ClassNotFoundException {
- try {
- return (Message) inputStream.readObject();
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void close() throws Exception {
- inputStream.close();
- outputStream.close();
- socket.close();
- }
-}
diff --git a/common/src/module-info.java b/common/src/module-info.java
index bb555eb..7483ae2 100644
--- a/common/src/module-info.java
+++ b/common/src/module-info.java
@@ -1,3 +1,3 @@
module common {
- exports com.soroko.common.common;
+ exports com.soroko.common;
}
\ No newline at end of file
diff --git a/server/server.iml b/server/server.iml
new file mode 100644
index 0000000..99e828d
--- /dev/null
+++ b/server/server.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server/src/com/soroko/server/Server.java b/server/src/com/soroko/server/Server.java
index dbc1fe5..fc88f2a 100644
--- a/server/src/com/soroko/server/Server.java
+++ b/server/src/com/soroko/server/Server.java
@@ -1,57 +1,33 @@
package com.soroko.server;
+import com.soroko.common.*;
-import com.soroko.common.common.Message;
-import com.soroko.common.common.SendReceive;
-
-import java.io.IOException;
+import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
-import java.util.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
public class Server {
- private int port;
- private int reqCounter = 0;
- private long elapsedTime;
- private int helpCounter = 0;
- private int pingCounter = 0;
- private int requestsCounter = 0;
- private int popularCounter = 0;
-
- private Map requests = new HashMap<>();
+ public static final String SERVER_STORAGE_LOCATION
+ = "C:\\Users\\yuriy\\IdeaProjects\\socketLesson\\server\\src\\com\\soroko\\server\\";
+ private final int port;
+ private final List messages = new CopyOnWriteArrayList<>();
+ private final List connectionHandlers = new CopyOnWriteArrayList<>();
+ private final List fileMessages = new CopyOnWriteArrayList<>();
+ private final int fileSize;
+ private final int amountOfSymbols;
public Server(int port) {
this.port = port;
- }
-
- public String makeRequest(String text) {
- if (text.equals("/help")) {
- reqCounter++;
- requests.put("/help", ++helpCounter);
- return "/help - список доступных запросов и их описание\n" +
- "/ping - время ответа сервера\n" +
- "/requests - количество успешно обработанных запросов\n" +
- "/popular - название самого популярного запроса\n" +
- "/exit - завершить соединение с сервером";
- } else if (text.equals("/ping")) {
- reqCounter++;
- requests.put("ping", ++pingCounter);
- return elapsedTime + " ms";
- } else if (text.equals("/requests")) {
- reqCounter++;
- requests.put("requests", ++requestsCounter);
- return String.valueOf(reqCounter);
- } else if (text.equals("/popular")) {
- reqCounter++;
- requests.put("popular", ++popularCounter);
- return requests.entrySet().stream()
- .max(Comparator.comparingInt(Map.Entry::getValue))
- .orElseGet(null)
- .toString();
- } else {
- return "Невозможно обработать запрос";
- }
+ fileSize = 10;
+ amountOfSymbols = 200;
}
public void startServer() {
@@ -59,20 +35,179 @@ public void startServer() {
while (true) {
try {
Socket socket = serverSocket.accept();
- long startTime = System.nanoTime();
- SendReceive connetionHandler = new SendReceive(socket);
- Message fromClient = connetionHandler.receive();
- String fromServer = makeRequest(fromClient.getText());
- Message message = new Message("server");
- message.setText(fromServer);
- connetionHandler.send(message);
- elapsedTime = (System.nanoTime() - startTime) / 1_000_000L;
+ SendReceive connectionHandler = new SendReceive(socket);
+ connectionHandlers.add(connectionHandler);
+ new ThreadForClient(connectionHandler).start();
} catch (Exception e) {
- System.out.println("Проблема с соединением");
+ System.out.println("Проблема с установкой нового соединения");
}
}
} catch (IOException e) {
System.out.println("Ошибка запуска сервера");
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void copy(File source, File destination) throws IOException {
+ Files.copy(source.toPath(), destination.toPath());
+ }
+
+ private class ThreadForClient extends Thread {
+ private final SendReceive connectionHandler;
+ private boolean selfMessageIsActive;
+ private boolean loadFileFlag;
+
+ public ThreadForClient(SendReceive connectionHandler) {
+ this.connectionHandler = connectionHandler;
+ }
+
+ public synchronized void showFiles() {
+ Message message = new Message("server");
+ String intro = "Список доступных файлов:";
+ String fileInformation = fileMessages.stream()
+ .map(FileMessage::toString)
+ .collect(Collectors.joining(", "));
+ if (fileMessages.isEmpty()) {
+ message.setFilesAreEmpty(true);
+ message.setText("Доступных для скачивания файлов не обнаружено");
+ } else {
+ message.setText(intro + fileInformation);
+ }
+ try {
+ connectionHandler.send(message);
+ } catch (IOException e) {
+ connectionHandler.close();
+ }
+ }
+
+ public synchronized void loadFile(FileMessage fileMessage) {
+ int randomName = (int) (Math.random() * 1000);
+ char[] descriptionChars = fileMessage.getDescription().toCharArray();
+ File fileSource = new File(fileMessage.getFilePath());
+ String fileName = SERVER_STORAGE_LOCATION + fileSource.getName();
+ String answer;
+ File fileDestination;
+ Path path = Paths.get(fileName);
+ if (Files.exists(path)) {
+ fileDestination = new File((SERVER_STORAGE_LOCATION + randomName + fileSource.getName()));
+ } else {
+ fileDestination = new File(fileName);
+ }
+ Message message = new Message("server");
+ if (!fileSource.isDirectory() && fileSource.exists()) {
+ if (fileMessage.getSize() <= fileSize && descriptionChars.length <= amountOfSymbols) {
+ try {
+ copy(fileSource, fileDestination);
+ if (fileDestination.isFile()) {
+ fileMessage.setFilePath(fileDestination.getName());
+ fileMessages.add(fileMessage);
+ message.setFilesAreEmpty(false);
+ }
+ answer = "Файл " + fileDestination.getName() + " был успешно загружен";
+ message.setText(answer);
+ loadFileFlag = true;
+ selfMessageIsActive = true;
+ messages.add(message);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ } else {
+ answer = "Файл по указанному пути не найден"
+ + " или содержит слишком большой объем информации";
+ message.setText(answer);
+ try {
+ connectionHandler.send(message);
+ } catch (IOException e) {
+ connectionHandler.close();
+ }
+ }
+ }
+
+ synchronized void saveFile(FileMessage fileMessage) {
+ File fileSource = new File((SERVER_STORAGE_LOCATION + fileMessage.getDescription()));
+ File fileDestination = new File(fileMessage.getFilePath() + fileMessage.getDescription());
+ String answer;
+ Message message = new Message("server");
+ if (fileMessages.isEmpty()) message.setFilesAreEmpty(true);
+ try {
+ copy(fileSource, fileDestination);
+ if (fileDestination.isFile()) {
+ answer = "Файл " + fileDestination.getName() + " был успешно сохранен";
+ message.setText(answer);
+ connectionHandler.send(message);
+ }
+ } catch (IOException e) {
+ answer = "Неверное имя файла или файла нет в списке, " +
+ "либо файл с таким именем уже существует";
+ message.setText(answer);
+ try {
+ connectionHandler.send(message);
+ } catch (IOException ex) {
+ connectionHandler.close();
+ }
+ }
+ }
+
+ public FileMessage createFileMessage() {
+ FileMessage fileMessage;
+ try {
+ fileMessage = connectionHandler.receiveFileDescription();
+ } catch (IOException e) {
+ connectionHandlers.remove(connectionHandler);
+ return null;
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return fileMessage;
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ Message fromClient;
+ try {
+ fromClient = connectionHandler.receive();
+ } catch (IOException e) {
+ connectionHandlers.remove(connectionHandler);
+ return;
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (fromClient != null && !fromClient.getText().equals("/files") &&
+ !fromClient.getText().equals("/loadfile") && !fromClient.getText().equals("/savefile") &&
+ !fromClient.getText().isEmpty()) {
+ Message message = new Message("server: " + fromClient.getSender());
+ message.setText(fromClient.getSentAt() + " " + fromClient.getSender() + ": " + fromClient.getText());
+ messages.add(message);
+ } else if (Objects.requireNonNull(fromClient).getText().equals("/files")) {
+ selfMessageIsActive = true;
+ showFiles();
+ } else if (fromClient.getText().equals("/loadfile")) {
+ selfMessageIsActive = true;
+ FileMessage fileMessage = createFileMessage();
+ loadFile(Objects.requireNonNull(fileMessage));
+ } else if (fromClient.getText().equals("/savefile")) {
+ selfMessageIsActive = true;
+ showFiles();
+ FileMessage fileMessage = createFileMessage();
+ saveFile(Objects.requireNonNull(fileMessage));
+ }
+ Message message = null;
+ if (!messages.isEmpty()) message = messages.getLast();
+ for (SendReceive handler : connectionHandlers) {
+ try {
+ if ((handler != this.connectionHandler && !this.selfMessageIsActive) ||
+ (handler == this.connectionHandler && this.loadFileFlag)) {
+ handler.send(Objects.requireNonNull(message));
+ }
+ } catch (IOException e) {
+ connectionHandlers.remove(handler);
+ }
+ }
+ selfMessageIsActive = false;
+ loadFileFlag = false;
+ }
}
}
-}
+}
\ No newline at end of file