diff --git a/README.md b/README.md
index 4314260..147f1dd 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,13 @@
-# java_homeworks
\ No newline at end of file
+### Torrent
+
+Для сборки выполнить
+
+```
+mvn install
+
+```
+
+В папке target появятся client.jar и server.jar .
+Клиенту при запуске можно передать имя хоста. По умолчанию localhost.
+
+У клиента есть команда `help`, показывающая список команд.
\ No newline at end of file
diff --git a/torrent/pom.xml b/torrent/pom.xml
new file mode 100644
index 0000000..c7926bf
--- /dev/null
+++ b/torrent/pom.xml
@@ -0,0 +1,99 @@
+
+ 4.0.0
+
+ 1
+ torrent
+ 0.0.1-SNAPSHOT
+
+
+ torrent
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ maven-compiler-plugin
+
+ 1.9
+ 1.9
+ UTF-8
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ torrent_server
+ package
+
+ single
+
+
+
+
+
+ torrent.server.Main
+
+
+
+
+ jar-with-dependencies
+
+ server
+ false
+
+
+
+ torrent_client
+ package
+
+ single
+
+
+
+
+
+ torrent.client.Main
+
+
+
+
+ jar-with-dependencies
+
+ client
+ false
+
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.7
+
+
+
+
+ org.apache.commons
+ commons-io
+ 1.3.2
+
+
+
diff --git a/torrent/src/main/java/torrent/client/FileProblemException.java b/torrent/src/main/java/torrent/client/FileProblemException.java
new file mode 100644
index 0000000..6cc213d
--- /dev/null
+++ b/torrent/src/main/java/torrent/client/FileProblemException.java
@@ -0,0 +1,14 @@
+package torrent.client;
+
+public class FileProblemException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public FileProblemException(String string) {
+ super(string);
+ }
+
+}
diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java
new file mode 100644
index 0000000..3a81e08
--- /dev/null
+++ b/torrent/src/main/java/torrent/client/FilesDownloader.java
@@ -0,0 +1,64 @@
+package torrent.client;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import torrent.client.FilesHolder.FileStatus;
+
+public class FilesDownloader {
+
+ private FilesHolder filesHolder;
+ private SocketAddress toServer;
+
+ private ExecutorService pool = Executors.newCachedThreadPool();
+
+ private Map> fileDownloadsFutures = new HashMap<>();
+
+ public FilesDownloader(FilesHolder stm, SocketAddress toServer) {
+ this.filesHolder = stm;
+ this.toServer = toServer;
+ stm.fileStatus.forEach((id, status) -> {
+ if (status == FileStatus.Downloading ) {
+ try {
+ startFileDownload(id);
+ } catch (IOException e) {
+ System.err.println("Failed. to start download at startup");
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ public boolean startFileDownload(int fileId) throws IOException {
+ if (fileDownloadsFutures.containsKey(fileId)) {
+ return false;
+ }
+
+ filesHolder.fileStatus.put(fileId, FileStatus.Downloading);
+
+ SingleFileDownloader downloader = new SingleFileDownloader(toServer, filesHolder, fileId, this);
+ fileDownloadsFutures.put(fileId, pool.submit(downloader));
+ return true;
+ }
+
+ public void stopFileDownload(int fileId) {
+ if (!filesHolder.fileStatus.containsKey(fileId)) {
+ throw new IllegalStateException("This file wasn't been downloading");
+ }
+
+ if (filesHolder.fileStatus.get(fileId) != FileStatus.Downloading) {
+ return;
+ }
+
+ fileDownloadsFutures.get(fileId).cancel(true);
+ fileDownloadsFutures.remove(fileId);
+
+ filesHolder.fileStatus.put(fileId, FileStatus.Paused);
+ }
+
+}
diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java
new file mode 100644
index 0000000..e21f102
--- /dev/null
+++ b/torrent/src/main/java/torrent/client/FilesHolder.java
@@ -0,0 +1,216 @@
+package torrent.client;
+
+import java.io.Closeable;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class FilesHolder implements Closeable{
+
+ public final int pieceSize = 0xA00000;
+ //рабочие данные
+
+ public enum FileStatus {Complete, Downloading, Paused};
+
+ private final Map files = new ConcurrentHashMap<>();
+ public Map filePaths = new ConcurrentHashMap<>();
+
+ public Map fileStatus = new ConcurrentHashMap<>();
+ public Map> completePieces = new ConcurrentHashMap<>();
+ public Map fileSize = new ConcurrentHashMap<>();
+ //
+
+ ObjectMapper mapper = new ObjectMapper();
+ private final Path mapPath;
+ private final Path filePathsPath;
+ private final Path fileStatusPath;
+ private final Path comletePiecesPath;
+ private final Path fileSizePath;
+
+ public int numParts(int fileId) {
+ return Math.toIntExact((fileSize.get(fileId) + pieceSize - 1) / pieceSize);
+ }
+
+ public int pieceOffset(int fileId, int numPart) {
+ return pieceSize * numPart;
+ }
+
+ public int pieceLenght(int fileId, int numPart) {
+ int file_length;
+ file_length = Math.toIntExact(fileSize.get(fileId));
+ return (numPart + 1) * pieceSize <= file_length
+ ? pieceSize
+ : file_length - numPart * pieceSize;
+ }
+
+ public FilesHolder(String path) throws IOException {
+ mapPath = Paths.get(path);
+ filePathsPath = mapPath.resolve("filepaths");
+ fileStatusPath = mapPath.resolve("filesStatus");
+ comletePiecesPath = mapPath.resolve("completePieces");
+ fileSizePath = mapPath.resolve("fileSizes");
+ load();
+ }
+
+ public void writeMaps() throws IOException {
+ if (!Files.exists(mapPath))
+ Files.createDirectories(mapPath);
+
+ filePathsPath.toFile().createNewFile();
+ mapper.writeValue(filePathsPath.toFile(), filePaths);
+
+ fileStatusPath.toFile().createNewFile();
+ mapper.writeValue(fileStatusPath.toFile(), fileStatus);
+
+ comletePiecesPath.toFile().createNewFile();
+ mapper.writeValue(comletePiecesPath.toFile(), completePieces);
+
+ fileSizePath.toFile().createNewFile();
+ mapper.writeValue(fileSizePath.toFile(), fileSize);
+ }
+
+ public void save() throws JsonGenerationException, JsonMappingException, IOException {
+ writeMaps();
+ }
+
+ public void load() throws JsonGenerationException, JsonMappingException, IOException {
+ if (filePathsPath.toFile().exists())
+ filePaths = mapper.readValue(filePathsPath.toFile(), new TypeReference