From 31f0278d5f0bf8836965f5c07b501bafaaf602ff Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 7 Dec 2018 19:53:03 +0300 Subject: [PATCH 01/25] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D1=82=D1=8C=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=20=D0=BD=D0=B0=20=D0=B0=D1=81=D0=B8=D0=BD=D1=85=D1=80?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 36 ++++++++++ .../common/ConcreteRequestHandler.java | 8 +++ .../common/RequestCompletionHandler.java | 37 ++++++++++ .../java/torrent/common/RequestHandler.java | 10 +++ .../src/main/java/torrent/server/Main.java | 26 +++++++ .../torrent/server/ServerRequestHandler.java | 69 +++++++++++++++++++ 6 files changed, 186 insertions(+) create mode 100644 torrent/pom.xml create mode 100644 torrent/src/main/java/torrent/common/ConcreteRequestHandler.java create mode 100644 torrent/src/main/java/torrent/common/RequestCompletionHandler.java create mode 100644 torrent/src/main/java/torrent/common/RequestHandler.java create mode 100644 torrent/src/main/java/torrent/server/Main.java create mode 100644 torrent/src/main/java/torrent/server/ServerRequestHandler.java diff --git a/torrent/pom.xml b/torrent/pom.xml new file mode 100644 index 0000000..2ee9a84 --- /dev/null +++ b/torrent/pom.xml @@ -0,0 +1,36 @@ + + 4.0.0 + + 1 + torrent + 0.0.1-SNAPSHOT + jar + + torrent + http://maven.apache.org + + + UTF-8 + + + + + maven-compiler-plugin + + 1.9 + 1.9 + UTF-8 + + + + + + + junit + junit + 3.8.1 + test + + + diff --git a/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java b/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java new file mode 100644 index 0000000..74af5ba --- /dev/null +++ b/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java @@ -0,0 +1,8 @@ +package torrent.common; + +import java.io.DataInputStream; +import java.io.DataOutputStream; + +public interface ConcreteRequestHandler { + boolean computeResult(DataInputStream in, DataOutputStream out); +} diff --git a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java new file mode 100644 index 0000000..01377bf --- /dev/null +++ b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java @@ -0,0 +1,37 @@ +package torrent.common; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class RequestCompletionHandler implements CompletionHandler { + private AsynchronousSocketChannel clientChannel; + + public RequestCompletionHandler(AsynchronousSocketChannel sch) { + clientChannel = sch; + } + + @Override + public void completed(Integer result, RequestHandler handler) { + if (handler.inputMessageComplete()) { + ByteBuffer toTransmit = handler.getTransmittingBuffer(); + clientChannel.write(toTransmit, null, new CompletionHandler() { + + @Override + public void completed(Integer result, Object attachment) { + if (!toTransmit.hasRemaining()) { + clientChannel.write(toTransmit, null, this); + } + } + + @Override + public void failed(Throwable exc, Object attachment) {} + }); + } else { + clientChannel.read(handler.getReceivingBuffer(), handler, this); + } + } + + @Override + public void failed(Throwable exc, RequestHandler attachment) {} +} diff --git a/torrent/src/main/java/torrent/common/RequestHandler.java b/torrent/src/main/java/torrent/common/RequestHandler.java new file mode 100644 index 0000000..adefb6f --- /dev/null +++ b/torrent/src/main/java/torrent/common/RequestHandler.java @@ -0,0 +1,10 @@ +package torrent.common; + +import java.nio.ByteBuffer; + +public interface RequestHandler { + ByteBuffer getReceivingBuffer(); + boolean inputMessageComplete(); + ByteBuffer getTransmittingBuffer(); + boolean allOutputSent(); +} diff --git a/torrent/src/main/java/torrent/server/Main.java b/torrent/src/main/java/torrent/server/Main.java new file mode 100644 index 0000000..eca792a --- /dev/null +++ b/torrent/src/main/java/torrent/server/Main.java @@ -0,0 +1,26 @@ +package torrent.server; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.ExecutionException; + +import torrent.common.RequestCompletionHandler; +import torrent.common.RequestHandler; + +public class Main { + public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { + AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); + srvChannel.bind(new InetSocketAddress(8081)); + RequestHandler handler = new ServerRequestHandler(); + + + while (true) { + AsynchronousSocketChannel clientChannel = srvChannel.accept().get(); + + clientChannel.read(handler.getReceivingBuffer(), handler, + new RequestCompletionHandler(clientChannel)); + } + } +} diff --git a/torrent/src/main/java/torrent/server/ServerRequestHandler.java b/torrent/src/main/java/torrent/server/ServerRequestHandler.java new file mode 100644 index 0000000..1131b83 --- /dev/null +++ b/torrent/src/main/java/torrent/server/ServerRequestHandler.java @@ -0,0 +1,69 @@ +package torrent.server; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import torrent.common.ConcreteRequestHandler; +import torrent.common.RequestHandler; + +public class ServerRequestHandler implements RequestHandler { + + private ByteBuffer inputBuffer = ByteBuffer.allocate(10); + private ByteBuffer outputBuffer = ByteBuffer.allocate(10); + + private ConcreteRequestHandler[] handlers; + + public ServerRequestHandler(ConcreteRequestHandler... handlers) { + this.handlers = handlers; + } + + @Override + public ByteBuffer getReceivingBuffer() { + inputBuffer.clear(); + return inputBuffer; + } + + @Override + public boolean inputMessageComplete() { + DataInputStream dInp = new DataInputStream( + new ByteArrayInputStream(inputBuffer.array(), + inputBuffer.arrayOffset(), + inputBuffer.limit() - inputBuffer.position())); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dOut = new DataOutputStream(bout); + + byte typeIndex; + try { + typeIndex = dInp.readByte(); + } catch (IOException e) { + return false; + } + + if (typeIndex > handlers.length) { + return false; + } + + if (!handlers[typeIndex - 1].computeResult(dInp, dOut)) { + return false; + } + + outputBuffer = ByteBuffer.wrap(bout.toByteArray()); + return true; + } + + @Override + public ByteBuffer getTransmittingBuffer() { + return outputBuffer; + } + + @Override + public boolean allOutputSent() { + // TODO Auto-generated method stub + return false; + } + +} From 3d6ba2c2275680c2302522cf8e5204dca405423d Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 12 Dec 2018 15:39:35 +0300 Subject: [PATCH 02/25] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BB=D0=B8=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 15 ++++ .../java/torrent/client/FilesDownloader.java | 37 ++++++++ .../main/java/torrent/client/FilesHolder.java | 15 ++++ .../src/main/java/torrent/client/Main.java | 86 +++++++++++++++++++ .../java/torrent/client/PieceDownloader.java | 53 ++++++++++++ .../java/torrent/client/SourcesUpdater.java | 50 +++++++++++ .../java/torrent/client/UpdatePerformer.java | 56 ++++++++++++ .../common/AbstractConcreteTaskHandler.java | 22 +++++ .../common/ConcreteRequestHandler.java | 8 -- .../torrent/common/ConcreteTaskHandler.java | 12 +++ .../java/torrent/common/FileInformation.java | 21 +++++ .../common/RequestCompletionHandler.java | 38 ++++++-- .../java/torrent/common/RequestHandler.java | 10 --- .../ServerRequestHandler.java | 41 ++++----- .../java/torrent/common/StorageManager.java | 41 +++++++++ .../main/java/torrent/server/ListHandler.java | 40 +++++++++ .../src/main/java/torrent/server/Main.java | 48 ++++++++++- .../torrent/server/OldClientsCleaner.java | 44 ++++++++++ .../main/java/torrent/server/ServerData.java | 13 +++ .../java/torrent/server/SourcesHandler.java | 52 +++++++++++ .../java/torrent/server/UpdateHandler.java | 56 ++++++++++++ .../java/torrent/server/UploadHandler.java | 44 ++++++++++ 22 files changed, 749 insertions(+), 53 deletions(-) create mode 100644 torrent/src/main/java/torrent/client/FilesDownloader.java create mode 100644 torrent/src/main/java/torrent/client/FilesHolder.java create mode 100644 torrent/src/main/java/torrent/client/Main.java create mode 100644 torrent/src/main/java/torrent/client/PieceDownloader.java create mode 100644 torrent/src/main/java/torrent/client/SourcesUpdater.java create mode 100644 torrent/src/main/java/torrent/client/UpdatePerformer.java create mode 100644 torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java delete mode 100644 torrent/src/main/java/torrent/common/ConcreteRequestHandler.java create mode 100644 torrent/src/main/java/torrent/common/ConcreteTaskHandler.java create mode 100644 torrent/src/main/java/torrent/common/FileInformation.java delete mode 100644 torrent/src/main/java/torrent/common/RequestHandler.java rename torrent/src/main/java/torrent/{server => common}/ServerRequestHandler.java (56%) create mode 100644 torrent/src/main/java/torrent/common/StorageManager.java create mode 100644 torrent/src/main/java/torrent/server/ListHandler.java create mode 100644 torrent/src/main/java/torrent/server/OldClientsCleaner.java create mode 100644 torrent/src/main/java/torrent/server/ServerData.java create mode 100644 torrent/src/main/java/torrent/server/SourcesHandler.java create mode 100644 torrent/src/main/java/torrent/server/UpdateHandler.java create mode 100644 torrent/src/main/java/torrent/server/UploadHandler.java diff --git a/torrent/pom.xml b/torrent/pom.xml index 2ee9a84..066da2d 100644 --- a/torrent/pom.xml +++ b/torrent/pom.xml @@ -32,5 +32,20 @@ 3.8.1 test + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.7 + + + + + commons-cli + commons-cli + 1.4 + + 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..c97ec99 --- /dev/null +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -0,0 +1,37 @@ +package torrent.client; + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import torrent.common.StorageManager; + +public class FilesDownloader implements Runnable { + + StorageManager stm; + Socket toServer; + + Map> fileSources = new HashMap<>(); + Map> avaliablePieces = new HashMap<>(); + Map> downloadingPieces = new HashMap<>(); + + public FilesDownloader(StorageManager stm, Socket toServer) { + this.stm = stm; + this.toServer = toServer; + } + + @Override + public void run() { + try { + Thread.sleep(10000); + + + } catch (InterruptedException e) { + return; + } + } + +} 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..57cb684 --- /dev/null +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -0,0 +1,15 @@ +package torrent.client; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class FilesHolder { + + public final int pieceSize = 0xA00000; + + public Map files = new HashMap<>(); + public Set completedFiles = new HashSet<>(); + public Map> completePieces = new HashMap<>(); +} diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java new file mode 100644 index 0000000..34e1183 --- /dev/null +++ b/torrent/src/main/java/torrent/client/Main.java @@ -0,0 +1,86 @@ +package torrent.client; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.ExecutionException; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import torrent.common.ConcreteTaskHandler; +import torrent.common.RequestCompletionHandler; +import torrent.common.ServerRequestHandler; +import torrent.common.StorageManager; +import torrent.server.ListHandler; +import torrent.server.OldClientsCleaner; +import torrent.server.ServerData; +import torrent.server.SourcesHandler; +import torrent.server.UpdateHandler; +import torrent.server.UploadHandler; + +public class Main { + + final Socket toServer = new Socket("localhost", 8081); + + final static int myPort = 8082; + final static String helpMessage = + "This is torrent client"; + + public Main() throws IOException { + + } + + public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException, InterruptedException, ExecutionException { + + StorageManager storageManager = new StorageManager<>(FilesHolder.class, "downloads/state"); + + Options options = new Options(); + options.addOption("list", "list files, known to server"); + options.addOption("upload", true, "upload file to server"); + + AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); + srvChannel.bind(new InetSocketAddress(myPort)); + + ConcreteTaskHandler[] concreteHandlers = new ConcreteTaskHandler[] {}; + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + System.out.println("Saving client state..."); + try { + storageManager.lock.writeLock().lock(); + storageManager.save(); + storageManager.lock.writeLock().unlock(); + } catch (JsonGenerationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JsonMappingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + + + while (true) { + AsynchronousSocketChannel clientChannel = srvChannel.accept().get(); + + ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); + + clientChannel.read(handler.getReceivingBuffer(), handler, + new RequestCompletionHandler(clientChannel)); + } + + } + +} diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java new file mode 100644 index 0000000..3b8cecc --- /dev/null +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -0,0 +1,53 @@ +package torrent.client; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import torrent.common.StorageManager; + +public class PieceDownloader implements CompletionHandler { + private int fileId; + private int pieceOffset, pieceLength; + private int pieceIdx; + private FilesDownloader filesDownloader; + + private ByteBuffer buffer; + + public PieceDownloader(FilesDownloader filesDownloader, int fileId, int pieceIdx) { + this.filesDownloader = filesDownloader; + this.fileId = fileId; + + this.pieceIdx = pieceIdx; + int piece_size = filesDownloader.stm.data.pieceSize; + int file_length = filesDownloader.stm.data.files.get(fileId).length; + + this.pieceOffset = pieceIdx * piece_size; + this.pieceLength = (pieceIdx + 1) * piece_size >= file_length + ? piece_size + : (pieceIdx + 1) * piece_size - file_length; + + buffer = ByteBuffer.wrap(filesDownloader.stm.data.files.get(fileId), pieceOffset, pieceLength); + } + + public ByteBuffer getBuffer() { + return buffer; + } + + @Override + public void completed(Integer result, AsynchronousSocketChannel attachment) { + if (buffer.hasRemaining()) { + attachment.read(buffer, attachment, this); + } else { + filesDownloader.stm.data.completePieces.get(fileId).add(pieceIdx); + filesDownloader.downloadingPieces.get(fileId).remove(pieceIdx); + } + } + + @Override + public void failed(Throwable exc, AsynchronousSocketChannel attachment) { + // TODO Auto-generated method stub + + } + + +} diff --git a/torrent/src/main/java/torrent/client/SourcesUpdater.java b/torrent/src/main/java/torrent/client/SourcesUpdater.java new file mode 100644 index 0000000..4274c8f --- /dev/null +++ b/torrent/src/main/java/torrent/client/SourcesUpdater.java @@ -0,0 +1,50 @@ +package torrent.client; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; + +public class SourcesUpdater implements Runnable{ + + private Socket toServer; + private FilesDownloader filesDownloader; + + public SourcesUpdater(Socket toServer, FilesDownloader filesDownloader) { + this.toServer = toServer; + this.filesDownloader = filesDownloader; + } + + public void updateSources() { + DataOutputStream out; + DataInputStream in; + try { + out = new DataOutputStream(toServer.getOutputStream()); + in = new DataInputStream(toServer.getInputStream()); + for (Integer fileId : filesDownloader.avaliablePieces.keySet()) { + out.writeByte(3); + out.writeInt(fileId); + + int clientsCount = in.readInt(); + List sources = new ArrayList<>(); + filesDownloader.fileSources.put(fileId, sources); + for (int i = 0; i < clientsCount; i++) { + sources.add(new InetSocketAddress(new InetAddress, in.readShort())); + } + } + } catch (IOException e) { + System.out.println("SourceUpdater: creation of output stream failed"); + } + + } + + @Override + public void run() { + // TODO Auto-generated method stub + + } +} diff --git a/torrent/src/main/java/torrent/client/UpdatePerformer.java b/torrent/src/main/java/torrent/client/UpdatePerformer.java new file mode 100644 index 0000000..e419a2b --- /dev/null +++ b/torrent/src/main/java/torrent/client/UpdatePerformer.java @@ -0,0 +1,56 @@ +package torrent.client; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; + +import torrent.common.StorageManager; + +public class UpdatePerformer implements Runnable { + + private Socket toServer; + private StorageManager stm; + private short myPort; + + public UpdatePerformer(StorageManager stm, Socket toServer, short myPort) { + this.stm = stm; + this.toServer = toServer; + this.myPort = myPort; + } + + @Override + public void run() { + + while(true) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + return; + } + + DataOutputStream out; + try { + out = new DataOutputStream(toServer.getOutputStream()); + out.writeByte(4); + out.writeShort(myPort); + } catch (IOException e) { + System.out.println("UpdatePerformer: creation of DataOutputStream failed"); + continue; + } + + stm.lock.readLock().lock(); + try { + out.writeInt(stm.data.completePieces.size()); + for (Integer fileId : stm.data.completePieces.keySet()) { + out.writeInt(fileId); + } + } catch (IOException e) { + System.out.println("UpdatePerformer failed"); + continue; + } finally { + stm.lock.readLock().unlock(); + } + } + } + +} diff --git a/torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java b/torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java new file mode 100644 index 0000000..a6a37ba --- /dev/null +++ b/torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java @@ -0,0 +1,22 @@ +package torrent.common; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import torrent.common.ServerRequestHandler.MessageProcessStatus; + +public abstract class AbstractConcreteTaskHandler implements ConcreteTaskHandler{ + protected final StorageManager storage; + + public AbstractConcreteTaskHandler(StorageManager stm) { + storage = stm; + } + + @Override + public abstract MessageProcessStatus computeResult( + DataInputStream in, + DataOutputStream out, + InetSocketAddress clientInf); +} diff --git a/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java b/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java deleted file mode 100644 index 74af5ba..0000000 --- a/torrent/src/main/java/torrent/common/ConcreteRequestHandler.java +++ /dev/null @@ -1,8 +0,0 @@ -package torrent.common; - -import java.io.DataInputStream; -import java.io.DataOutputStream; - -public interface ConcreteRequestHandler { - boolean computeResult(DataInputStream in, DataOutputStream out); -} diff --git a/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java b/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java new file mode 100644 index 0000000..db48e73 --- /dev/null +++ b/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java @@ -0,0 +1,12 @@ +package torrent.common; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import torrent.common.ServerRequestHandler.MessageProcessStatus; + +public interface ConcreteTaskHandler { + MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf); +} diff --git a/torrent/src/main/java/torrent/common/FileInformation.java b/torrent/src/main/java/torrent/common/FileInformation.java new file mode 100644 index 0000000..9786fc6 --- /dev/null +++ b/torrent/src/main/java/torrent/common/FileInformation.java @@ -0,0 +1,21 @@ +package torrent.common; + +import java.net.InetSocketAddress; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class FileInformation { + public int id; + public String name; + public long size; + public Set clients; + + public FileInformation(int id2, String name2, long size2, List clients2) { + id = id2; + name = name2; + size = size2; + clients = new HashSet<>(); + clients.addAll(clients2); + } +} diff --git a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java index 01377bf..5aa0476 100644 --- a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java +++ b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java @@ -1,10 +1,14 @@ package torrent.common; +import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; -public class RequestCompletionHandler implements CompletionHandler { +import torrent.common.ServerRequestHandler.MessageProcessStatus; + +public class RequestCompletionHandler implements CompletionHandler { private AsynchronousSocketChannel clientChannel; public RequestCompletionHandler(AsynchronousSocketChannel sch) { @@ -12,26 +16,46 @@ public RequestCompletionHandler(AsynchronousSocketChannel sch) { } @Override - public void completed(Integer result, RequestHandler handler) { - if (handler.inputMessageComplete()) { + public void completed(Integer result, ServerRequestHandler handler) { + MessageProcessStatus status; + + try { + status = handler.messageProcessAttemp((InetSocketAddress) clientChannel.getRemoteAddress()); + } catch (IOException e) { + return; + } + + if (status == MessageProcessStatus.SUCCESS) { ByteBuffer toTransmit = handler.getTransmittingBuffer(); clientChannel.write(toTransmit, null, new CompletionHandler() { @Override public void completed(Integer result, Object attachment) { - if (!toTransmit.hasRemaining()) { + if (toTransmit.hasRemaining()) { clientChannel.write(toTransmit, null, this); + } else { + try { + clientChannel.close(); + } catch (IOException e) { + System.out.println("RequestCompletionHandler: Error while closing channel: " + e.getMessage()); + } } } @Override - public void failed(Throwable exc, Object attachment) {} + public void failed(Throwable exc, Object attachment) { + System.out.println("Fail while transmitting data: " + exc.getMessage()); + } }); - } else { + } + + if (status == MessageProcessStatus.INCOMPLETE){ clientChannel.read(handler.getReceivingBuffer(), handler, this); } } @Override - public void failed(Throwable exc, RequestHandler attachment) {} + public void failed(Throwable exc, ServerRequestHandler attachment) { + System.out.println("Fail while receiving data: " + exc.getMessage()); + } } diff --git a/torrent/src/main/java/torrent/common/RequestHandler.java b/torrent/src/main/java/torrent/common/RequestHandler.java deleted file mode 100644 index adefb6f..0000000 --- a/torrent/src/main/java/torrent/common/RequestHandler.java +++ /dev/null @@ -1,10 +0,0 @@ -package torrent.common; - -import java.nio.ByteBuffer; - -public interface RequestHandler { - ByteBuffer getReceivingBuffer(); - boolean inputMessageComplete(); - ByteBuffer getTransmittingBuffer(); - boolean allOutputSent(); -} diff --git a/torrent/src/main/java/torrent/server/ServerRequestHandler.java b/torrent/src/main/java/torrent/common/ServerRequestHandler.java similarity index 56% rename from torrent/src/main/java/torrent/server/ServerRequestHandler.java rename to torrent/src/main/java/torrent/common/ServerRequestHandler.java index 1131b83..f839ea5 100644 --- a/torrent/src/main/java/torrent/server/ServerRequestHandler.java +++ b/torrent/src/main/java/torrent/common/ServerRequestHandler.java @@ -1,38 +1,37 @@ -package torrent.server; +package torrent.common; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; -import torrent.common.ConcreteRequestHandler; -import torrent.common.RequestHandler; - -public class ServerRequestHandler implements RequestHandler { +public class ServerRequestHandler{ + + private ByteBuffer inputBuffer = ByteBuffer.allocate(1000); + private ByteBuffer outputBuffer; - private ByteBuffer inputBuffer = ByteBuffer.allocate(10); - private ByteBuffer outputBuffer = ByteBuffer.allocate(10); + public enum MessageProcessStatus {INCOMPLETE, ERROR, SUCCESS}; - private ConcreteRequestHandler[] handlers; + private ConcreteTaskHandler[] handlers; - public ServerRequestHandler(ConcreteRequestHandler... handlers) { + public ServerRequestHandler(ConcreteTaskHandler[] handlers) { this.handlers = handlers; } - @Override public ByteBuffer getReceivingBuffer() { inputBuffer.clear(); return inputBuffer; } - @Override - public boolean inputMessageComplete() { + public MessageProcessStatus messageProcessAttemp(InetSocketAddress clientInf) { DataInputStream dInp = new DataInputStream( new ByteArrayInputStream(inputBuffer.array(), inputBuffer.arrayOffset(), inputBuffer.limit() - inputBuffer.position())); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dOut = new DataOutputStream(bout); @@ -40,30 +39,24 @@ public boolean inputMessageComplete() { try { typeIndex = dInp.readByte(); } catch (IOException e) { - return false; + return MessageProcessStatus.INCOMPLETE; } if (typeIndex > handlers.length) { - return false; + return MessageProcessStatus.ERROR; } - if (!handlers[typeIndex - 1].computeResult(dInp, dOut)) { - return false; + MessageProcessStatus status = handlers[typeIndex - 1].computeResult(dInp, dOut, clientInf); + if (status != MessageProcessStatus.SUCCESS) { + return status; } outputBuffer = ByteBuffer.wrap(bout.toByteArray()); - return true; + return status; } - @Override public ByteBuffer getTransmittingBuffer() { return outputBuffer; } - @Override - public boolean allOutputSent() { - // TODO Auto-generated method stub - return false; - } - } diff --git a/torrent/src/main/java/torrent/common/StorageManager.java b/torrent/src/main/java/torrent/common/StorageManager.java new file mode 100644 index 0000000..7f5c783 --- /dev/null +++ b/torrent/src/main/java/torrent/common/StorageManager.java @@ -0,0 +1,41 @@ +package torrent.common; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class StorageManager { + + private final Path savePath; + private final ObjectMapper mapper = new ObjectMapper(); + + public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public volatile T data; + private final Class Tclass; + + public StorageManager(Class Tclass, String savePath) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + this.savePath = Paths.get(savePath); + this.Tclass = Tclass; + try { + load(); + } catch (Exception e) { + data = Tclass.getConstructor().newInstance(); + } + } + + public void save() throws JsonGenerationException, JsonMappingException, IOException { + mapper.writeValue(savePath.toFile(), data); + } + + public void load() throws JsonParseException, JsonMappingException, IOException { + data = mapper.readValue(savePath.toFile(), Tclass); + } +} diff --git a/torrent/src/main/java/torrent/server/ListHandler.java b/torrent/src/main/java/torrent/server/ListHandler.java new file mode 100644 index 0000000..59a7eda --- /dev/null +++ b/torrent/src/main/java/torrent/server/ListHandler.java @@ -0,0 +1,40 @@ +package torrent.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import torrent.common.AbstractConcreteTaskHandler; +import torrent.common.FileInformation; +import torrent.common.ServerRequestHandler.MessageProcessStatus; +import torrent.common.StorageManager; + +public class ListHandler extends AbstractConcreteTaskHandler { + + public ListHandler(StorageManager stm) { + super(stm); + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf){ + storage.lock.readLock().lock(); + try { + out.writeInt(storage.data.map.size()); + + for(FileInformation d : storage.data.map.values()) { + out.writeInt(d.id); + out.writeUTF(d.name); + out.writeLong(d.size); + } + } catch (IOException e) { + return MessageProcessStatus.ERROR; + } finally { + storage.lock.readLock().unlock(); + } + + return MessageProcessStatus.SUCCESS; + } + +} diff --git a/torrent/src/main/java/torrent/server/Main.java b/torrent/src/main/java/torrent/server/Main.java index eca792a..5e427cf 100644 --- a/torrent/src/main/java/torrent/server/Main.java +++ b/torrent/src/main/java/torrent/server/Main.java @@ -1,24 +1,64 @@ package torrent.server; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.util.concurrent.ExecutionException; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import torrent.common.ConcreteTaskHandler; import torrent.common.RequestCompletionHandler; -import torrent.common.RequestHandler; +import torrent.common.ServerRequestHandler; +import torrent.common.StorageManager; public class Main { - public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { + public static void main(String[] args) throws IOException, InterruptedException, ExecutionException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + StorageManager storageManager = new StorageManager<>(ServerData.class, "serverFile"); + AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); srvChannel.bind(new InetSocketAddress(8081)); - RequestHandler handler = new ServerRequestHandler(); + + ConcreteTaskHandler[] concreteHandlers = new ConcreteTaskHandler[] { + new ListHandler(storageManager), + new UploadHandler(storageManager), + new SourcesHandler(storageManager), + new UpdateHandler(storageManager)}; + + Thread cleanThread = new Thread(new OldClientsCleaner(storageManager)); + cleanThread.start(); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + cleanThread.interrupt(); + System.out.println("Saving server state..."); + try { + storageManager.lock.writeLock().lock(); + storageManager.save(); + storageManager.lock.writeLock().unlock(); + } catch (JsonGenerationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JsonMappingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); while (true) { AsynchronousSocketChannel clientChannel = srvChannel.accept().get(); - + + ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); + clientChannel.read(handler.getReceivingBuffer(), handler, new RequestCompletionHandler(clientChannel)); } diff --git a/torrent/src/main/java/torrent/server/OldClientsCleaner.java b/torrent/src/main/java/torrent/server/OldClientsCleaner.java new file mode 100644 index 0000000..877cec5 --- /dev/null +++ b/torrent/src/main/java/torrent/server/OldClientsCleaner.java @@ -0,0 +1,44 @@ +package torrent.server; + +import torrent.common.StorageManager; + +public class OldClientsCleaner implements Runnable { + + public final long updateTime = 5 * 60 * 1000; + + private StorageManager storage; + + public OldClientsCleaner(StorageManager storage) { + this.storage = storage; + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(updateTime); + } catch (InterruptedException e) { + return; + } + + storage.lock.writeLock().lock(); + long currTime = System.currentTimeMillis(); + + storage.data.lastClientUpdate + .entrySet() + .removeIf(ent -> currTime - ent.getValue() > updateTime); + + storage.data.map.values().stream().forEach(fInf -> + fInf.clients.removeIf(s -> !storage.data.lastClientUpdate.containsKey(s))); + + try { + storage.save(); + } catch (Exception e) { + System.out.println("Cleaner: error while saving. " + e.getMessage()); + } + + storage.lock.writeLock().unlock(); + } + } + +} diff --git a/torrent/src/main/java/torrent/server/ServerData.java b/torrent/src/main/java/torrent/server/ServerData.java new file mode 100644 index 0000000..8684f21 --- /dev/null +++ b/torrent/src/main/java/torrent/server/ServerData.java @@ -0,0 +1,13 @@ +package torrent.server; + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; + +import torrent.common.FileInformation; + +public class ServerData { + public int filesCount = 0; + public Map map = new HashMap<>(); + public Map lastClientUpdate = new HashMap<>(); +} diff --git a/torrent/src/main/java/torrent/server/SourcesHandler.java b/torrent/src/main/java/torrent/server/SourcesHandler.java new file mode 100644 index 0000000..5729ccc --- /dev/null +++ b/torrent/src/main/java/torrent/server/SourcesHandler.java @@ -0,0 +1,52 @@ +package torrent.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.List; +import java.util.Set; + +import torrent.common.AbstractConcreteTaskHandler; +import torrent.common.ServerRequestHandler.MessageProcessStatus; +import torrent.common.StorageManager; + +public class SourcesHandler extends AbstractConcreteTaskHandler { + + public SourcesHandler(StorageManager stm) { + super(stm); + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { + storage.lock.readLock().lock(); + try { + int id = in.readInt(); + + if (!storage.data.map.containsKey(id)) { + return MessageProcessStatus.ERROR; + } + + Set clients = storage.data.map.get(id).clients; + + out.writeInt(clients.size()); + + for (InetSocketAddress addr : storage.data.map.get(id).clients) { + out.write(addr.getAddress().getAddress()); + out.writeShort(addr.getPort()); + } + + } catch (EOFException e){ + return MessageProcessStatus.INCOMPLETE; + } catch (IOException e){ + return MessageProcessStatus.ERROR; + } finally { + storage.lock.readLock().unlock(); + } + + return MessageProcessStatus.SUCCESS; + } + +} diff --git a/torrent/src/main/java/torrent/server/UpdateHandler.java b/torrent/src/main/java/torrent/server/UpdateHandler.java new file mode 100644 index 0000000..2b83b50 --- /dev/null +++ b/torrent/src/main/java/torrent/server/UpdateHandler.java @@ -0,0 +1,56 @@ +package torrent.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; + +import torrent.common.AbstractConcreteTaskHandler; +import torrent.common.FileInformation; +import torrent.common.ServerRequestHandler.MessageProcessStatus; +import torrent.common.StorageManager; + +public class UpdateHandler extends AbstractConcreteTaskHandler { + + public UpdateHandler(StorageManager stm) { + super(stm); + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { + storage.lock.writeLock().lock(); + try { + short clientPort = in.readShort(); + int count = in.readInt(); + + for (int i = 0; i < count; i++) { + int id = in.readInt(); + + InetSocketAddress clientAddr = new InetSocketAddress(clientInf.getAddress(), clientPort); + FileInformation fInf = storage.data.map.get(id); + + if (fInf == null) { + return MessageProcessStatus.ERROR; + } + + fInf.clients.add(clientAddr); + storage.data.lastClientUpdate.put(clientAddr, System.currentTimeMillis()); + } + storage.save(); + out.writeBoolean(true); + } catch (EOFException e){ + return MessageProcessStatus.INCOMPLETE; + } catch (IOException e) { + try { + out.writeBoolean(false); + } catch (IOException e1) {} + return MessageProcessStatus.ERROR; + } finally { + storage.lock.writeLock().unlock(); + } + + return MessageProcessStatus.SUCCESS; + } + +} diff --git a/torrent/src/main/java/torrent/server/UploadHandler.java b/torrent/src/main/java/torrent/server/UploadHandler.java new file mode 100644 index 0000000..17e4205 --- /dev/null +++ b/torrent/src/main/java/torrent/server/UploadHandler.java @@ -0,0 +1,44 @@ +package torrent.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Arrays; + +import torrent.common.AbstractConcreteTaskHandler; +import torrent.common.FileInformation; +import torrent.common.ServerRequestHandler.MessageProcessStatus; +import torrent.common.StorageManager; + +public class UploadHandler extends AbstractConcreteTaskHandler { + + public UploadHandler(StorageManager stm) { + super(stm); + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { + storage.lock.writeLock().lock(); + try { + String name = in.readUTF(); + long size = in.readLong(); + + int id = storage.data.filesCount++; + storage.data.map.put(id, new FileInformation(id, name, size, Arrays.asList(clientInf))); + storage.save(); + out.writeInt(id); + } catch (EOFException e) { + return MessageProcessStatus.INCOMPLETE; + } catch (IOException e){ + return MessageProcessStatus.ERROR; + } finally { + storage.lock.writeLock().unlock(); + } + + return MessageProcessStatus.SUCCESS; + } + +} From 8c04ed7a30c5556d637701bbf689407fd030ceb7 Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 13 Dec 2018 12:52:11 +0300 Subject: [PATCH 03/25] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=20=D1=81=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/torrent/client/FilesDownloader.java | 32 ++-- .../main/java/torrent/client/FilesHolder.java | 30 +++- .../java/torrent/client/PieceDownloader.java | 12 +- .../torrent/client/SingleFileDownloader.java | 143 ++++++++++++++++++ .../java/torrent/client/SourcesUpdater.java | 50 ------ 5 files changed, 198 insertions(+), 69 deletions(-) create mode 100644 torrent/src/main/java/torrent/client/SingleFileDownloader.java delete mode 100644 torrent/src/main/java/torrent/client/SourcesUpdater.java diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index c97ec99..358a71a 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -2,36 +2,38 @@ import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import torrent.common.StorageManager; -public class FilesDownloader implements Runnable { +public class FilesDownloader { StorageManager stm; - Socket toServer; + SocketAddress toServer; - Map> fileSources = new HashMap<>(); - Map> avaliablePieces = new HashMap<>(); - Map> downloadingPieces = new HashMap<>(); + ExecutorService pool = Executors.newFixedThreadPool(4); - public FilesDownloader(StorageManager stm, Socket toServer) { + Map fileDownloads = new HashMap<>(); + + public FilesDownloader(StorageManager stm, SocketAddress toServer) { this.stm = stm; this.toServer = toServer; } - - @Override - public void run() { - try { - Thread.sleep(10000); - - - } catch (InterruptedException e) { - return; + + public boolean startFileDownload(int fileId) { + if (fileDownloads.containsKey(fileId)) { + return false; } + SingleFileDownloader downloader = new SingleFileDownloader(toServer, stm, fileId); + fileDownloads.put(fileId, downloader); + pool.execute(downloader); + return true; } } diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 57cb684..391f991 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -1,15 +1,43 @@ package torrent.client; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + public class FilesHolder { public final int pieceSize = 0xA00000; - + + //рабочие данные public Map files = new HashMap<>(); + public Map filePaths = new HashMap<>(); + public Set completedFiles = new HashSet<>(); public Map> completePieces = new HashMap<>(); + // + + ObjectMapper mapper = new ObjectMapper(); + final Path mapPath = Paths.get("torrentData"); + final Path filesPaths = mapPath.resolve("filepaths"); + final Path compFiles = mapPath.resolve("completeFiles"); + final Path comPieces = mapPath.resolve("completePieces"); + + public int numParts(int fileId) { + return (files.get(fileId).length + pieceSize - 1) / pieceSize; + } + + public void save() throws JsonGenerationException, JsonMappingException, IOException { + mapper.writeValue(filesPaths.toFile(), filePaths); + mapper.writeValue(compFiles.toFile(), completedFiles); + mapper.writeValue(comPieces.toFile(), completePieces); + } + } diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index 3b8cecc..ca366ce 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -1,5 +1,6 @@ package torrent.client; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; @@ -9,11 +10,11 @@ public class PieceDownloader implements CompletionHandler stm; + private final int fileId; + private final int numPieces; + + private Random rand = new Random(3); + + private List fileSources = new ArrayList<>(); + private Map> pieceSources = new HashMap<>(); + private Map pieceChannels = new HashMap<>(); + + public SingleFileDownloader(SocketAddress srvAddr, StorageManager stm, int fileId) { + this.srvAddr = srvAddr; + this.stm = stm; + this.fileId = fileId; + + numPieces = stm.data.numParts(fileId); + } + + private void updateFileSources() { + DataOutputStream out; + DataInputStream in; + try (Socket toServer = new Socket()) { + toServer.connect(srvAddr); + out = new DataOutputStream(toServer.getOutputStream()); + in = new DataInputStream(toServer.getInputStream()); + out.writeByte(3); + out.writeInt(fileId); + + int clientsCount = in.readInt(); + fileSources.clear(); + byte[] ipBuf = new byte[4]; + for (int i = 0; i < clientsCount; i++) { + in.readFully(ipBuf); + + fileSources.add(new InetSocketAddress(InetAddress.getByAddress(ipBuf), + in.readShort())); + } + } catch (IOException e) { + System.out.println("SourceUpdater: creation of output stream failed while update file sources"); + } + } + + private void updatePieceSources() { + DataOutputStream out; + DataInputStream in; + + pieceSources.clear(); + for (int i = 0; i < numPieces; i++) { + pieceSources.put(i, new ArrayList<>()); + } + + for (SocketAddress addr : fileSources) { + try (Socket othClient = new Socket()) { + othClient.connect(addr, 10000); + out = new DataOutputStream(othClient.getOutputStream()); + in = new DataInputStream(othClient.getInputStream()); + + out.writeByte(1); + out.writeInt(fileId); + + int partCount = in.readInt(); + + for (int i = 0; i < partCount; i++) { + int partNum = in.readInt(); + pieceSources.get(partNum).add(addr); + } + + } catch (IOException e) { + System.out.println("SourceUpdater: failed in updatePieceSources"); + } + } + } + + private void dispatchPieceDownloaders() { + for (int i = 0; i < numPieces; i++) { + if (stm.data.completePieces.get(fileId).contains(i)) { + pieceChannels.remove(i); + continue; + } + + AsynchronousSocketChannel chan = pieceChannels.get(i); + if (chan != null) { + if (!chan.isOpen()) { + pieceChannels.remove(i); + } + } + + List sources = pieceSources.get(i); + if (!sources.isEmpty()) { + int rIdx = rand.nextInt(sources.size()); + + try { + chan = AsynchronousSocketChannel.open(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + chan.connect(sources.get(rIdx)); + PieceDownloader pdl = new PieceDownloader(this, fileId, i); + chan.read(pdl.getBuffer(), chan, pdl); + pieceChannels.put(i, chan); + } + } + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + return; + } + updateFileSources(); + updatePieceSources(); + dispatchPieceDownloaders(); + if (stm.data.completedFiles.contains(fileId)) { + break; + } + } + } +} diff --git a/torrent/src/main/java/torrent/client/SourcesUpdater.java b/torrent/src/main/java/torrent/client/SourcesUpdater.java deleted file mode 100644 index 4274c8f..0000000 --- a/torrent/src/main/java/torrent/client/SourcesUpdater.java +++ /dev/null @@ -1,50 +0,0 @@ -package torrent.client; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; - -public class SourcesUpdater implements Runnable{ - - private Socket toServer; - private FilesDownloader filesDownloader; - - public SourcesUpdater(Socket toServer, FilesDownloader filesDownloader) { - this.toServer = toServer; - this.filesDownloader = filesDownloader; - } - - public void updateSources() { - DataOutputStream out; - DataInputStream in; - try { - out = new DataOutputStream(toServer.getOutputStream()); - in = new DataInputStream(toServer.getInputStream()); - for (Integer fileId : filesDownloader.avaliablePieces.keySet()) { - out.writeByte(3); - out.writeInt(fileId); - - int clientsCount = in.readInt(); - List sources = new ArrayList<>(); - filesDownloader.fileSources.put(fileId, sources); - for (int i = 0; i < clientsCount; i++) { - sources.add(new InetSocketAddress(new InetAddress, in.readShort())); - } - } - } catch (IOException e) { - System.out.println("SourceUpdater: creation of output stream failed"); - } - - } - - @Override - public void run() { - // TODO Auto-generated method stub - - } -} From 7ec70ba9b00348739f8755ff96b1e9239b195c35 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 14 Dec 2018 23:23:58 +0300 Subject: [PATCH 04/25] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=20ReadWrit?= =?UTF-8?q?eLock=20=D0=B2=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B5.=20?= =?UTF-8?q?=D0=9C=D0=B0=D0=BF=D1=8B=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD?= =?UTF-8?q?=D1=8B=20concurrent.=20=D0=95=D1=89=D1=91=20=D0=B4=D0=BE=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=BE=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD?= =?UTF-8?q?=D0=B4=20=D0=B2=20REPL.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../torrent/client/FileProblemException.java | 9 ++ .../java/torrent/client/FilesDownloader.java | 33 ++-- .../main/java/torrent/client/FilesHolder.java | 106 ++++++++++-- .../src/main/java/torrent/client/Main.java | 54 +++---- .../java/torrent/client/PieceDownloader.java | 29 ++-- .../src/main/java/torrent/client/REPL.java | 151 ++++++++++++++++++ .../torrent/client/ServerFilesLister.java | 35 ++++ .../java/torrent/client/ServerProcess.java | 47 ++++++ .../torrent/client/SingleFileDownloader.java | 102 +++++++----- .../java/torrent/client/UpdatePerformer.java | 26 ++- .../main/java/torrent/client/Uploader.java | 30 ++++ 11 files changed, 497 insertions(+), 125 deletions(-) create mode 100644 torrent/src/main/java/torrent/client/FileProblemException.java create mode 100644 torrent/src/main/java/torrent/client/REPL.java create mode 100644 torrent/src/main/java/torrent/client/ServerFilesLister.java create mode 100644 torrent/src/main/java/torrent/client/ServerProcess.java create mode 100644 torrent/src/main/java/torrent/client/Uploader.java 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..f1a7b05 --- /dev/null +++ b/torrent/src/main/java/torrent/client/FileProblemException.java @@ -0,0 +1,9 @@ +package torrent.client; + +public class FileProblemException extends Exception { + + 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 index 358a71a..f31440b 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -1,39 +1,42 @@ package torrent.client; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.SocketAddress; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; - -import torrent.common.StorageManager; +import java.util.concurrent.Future; public class FilesDownloader { - StorageManager stm; + FilesHolder filesHolder; SocketAddress toServer; - ExecutorService pool = Executors.newFixedThreadPool(4); + ExecutorService pool = Executors.newCachedThreadPool(); - Map fileDownloads = new HashMap<>(); + Map> fileDownloadsFutures = new HashMap<>(); - public FilesDownloader(StorageManager stm, SocketAddress toServer) { - this.stm = stm; + public FilesDownloader(FilesHolder stm, SocketAddress toServer) { + this.filesHolder = stm; this.toServer = toServer; } public boolean startFileDownload(int fileId) { - if (fileDownloads.containsKey(fileId)) { + if (fileDownloadsFutures.containsKey(fileId)) { return false; } - SingleFileDownloader downloader = new SingleFileDownloader(toServer, stm, fileId); - fileDownloads.put(fileId, downloader); - pool.execute(downloader); + SingleFileDownloader downloader = new SingleFileDownloader(toServer, filesHolder, fileId); + //fileDownloads.put(fileId, downloader); + fileDownloadsFutures.put(fileId, pool.submit(downloader)); return true; } + + public void stopFileDownload(int fileId) { + if (!fileDownloadsFutures.containsKey(fileId)) { + return; + } + fileDownloadsFutures.get(fileId).cancel(true); + fileDownloadsFutures.remove(fileId); + } } diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 391f991..a88fd81 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -1,43 +1,121 @@ package torrent.client; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; public class FilesHolder { - + public final int pieceSize = 0xA00000; - + //public final ReadWriteLock lock = new ReentrantReadWriteLock(); //рабочие данные - public Map files = new HashMap<>(); - public Map filePaths = new HashMap<>(); - - public Set completedFiles = new HashSet<>(); - public Map> completePieces = new HashMap<>(); + public Map files = new ConcurrentHashMap<>(); + public Map filePaths = new ConcurrentHashMap<>(); + + public Set completedFiles = ConcurrentHashMap.newKeySet(); + public Map> completePieces = new ConcurrentHashMap<>(); // - + ObjectMapper mapper = new ObjectMapper(); - final Path mapPath = Paths.get("torrentData"); - final Path filesPaths = mapPath.resolve("filepaths"); - final Path compFiles = mapPath.resolve("completeFiles"); - final Path comPieces = mapPath.resolve("completePieces"); - + private final Path mapPath = Paths.get("torrentData"); + private final Path filesPaths = mapPath.resolve("filepaths"); + private final Path compFiles = mapPath.resolve("completeFiles"); + private final Path comPieces = mapPath.resolve("completePieces"); + public int numParts(int fileId) { return (files.get(fileId).length + pieceSize - 1) / pieceSize; } - + + public FilesHolder() throws JsonGenerationException, JsonMappingException, IOException { + load(); + } + public void save() throws JsonGenerationException, JsonMappingException, IOException { mapper.writeValue(filesPaths.toFile(), filePaths); mapper.writeValue(compFiles.toFile(), completedFiles); mapper.writeValue(comPieces.toFile(), completePieces); + + for (Entry ent : filePaths.entrySet()) { + try (FileOutputStream fout = new FileOutputStream(new File(ent.getValue()))) { + fout.write(files.get(ent.getKey())); + } + } } + public void save(int fileId) throws FileNotFoundException, IOException { + mapper.writeValue(filesPaths.toFile(), filePaths); + mapper.writeValue(compFiles.toFile(), completedFiles); + mapper.writeValue(comPieces.toFile(), completePieces); + + if (files.containsKey(fileId)) { + try (FileOutputStream fout = new FileOutputStream(new File(filePaths.get(fileId)))) { + fout.write(files.get(fileId)); + } + } + } + + public void load() throws JsonGenerationException, JsonMappingException, IOException { + filePaths = mapper.readValue(filesPaths.toFile(), filePaths.getClass()); + completedFiles = mapper.readValue(compFiles.toFile(), completedFiles.getClass()); + completePieces = mapper.readValue(comPieces.toFile(), completePieces.getClass()); + + for (Entry ent : filePaths.entrySet()) { + try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { + files.put(ent.getKey(), finp.readAllBytes()); + } + } + } + + public void deleteFile(int id) { + files.remove(id); + filePaths.remove(id); + completedFiles.remove(id); + completePieces.remove(id); + } + + public void addFileToDownload(int id, long size, String filePath) throws FileProblemException, FileNotFoundException, IOException { + if (files.containsKey(id)) { + throw new FileProblemException("id already used"); + } + if (filePaths.containsValue(filePath)) { + throw new FileProblemException("file with specified path used"); + } + files.put(id, new byte[Math.toIntExact(size)]); + filePaths.put(id, filePath); + completePieces.put(id, ConcurrentHashMap.newKeySet()); + save(-1); + } + + public void addExistingFile(int id, Path path) throws FileProblemException, FileNotFoundException, IOException { + if (files.containsKey(id)) { + throw new FileProblemException("id already used"); + } + if (filePaths.containsValue(path.toString())) { + throw new FileProblemException("file with specified path used"); + } + + try (FileInputStream finp = new FileInputStream(path.toFile())) { + files.put(id, finp.readAllBytes()); + } + filePaths.put(id, path.toString()); + completedFiles.add(id); + save(-1); + } + } diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java index 34e1183..b03ddac 100644 --- a/torrent/src/main/java/torrent/client/Main.java +++ b/torrent/src/main/java/torrent/client/Main.java @@ -4,10 +4,14 @@ import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; +import java.util.Scanner; import java.util.concurrent.ExecutionException; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -26,38 +30,27 @@ import torrent.server.UploadHandler; public class Main { - - final Socket toServer = new Socket("localhost", 8081); - - final static int myPort = 8082; + + final SocketAddress toServer = new InetSocketAddress("localhost", 8081); + final static String helpMessage = "This is torrent client"; - + public Main() throws IOException { - + } - + public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException, InterruptedException, ExecutionException { - - StorageManager storageManager = new StorageManager<>(FilesHolder.class, "downloads/state"); - - Options options = new Options(); - options.addOption("list", "list files, known to server"); - options.addOption("upload", true, "upload file to server"); - - AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); - srvChannel.bind(new InetSocketAddress(myPort)); - - ConcreteTaskHandler[] concreteHandlers = new ConcreteTaskHandler[] {}; - + + FilesHolder storageManager = new FilesHolder(); + + Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Saving client state..."); try { - storageManager.lock.writeLock().lock(); storageManager.save(); - storageManager.lock.writeLock().unlock(); } catch (JsonGenerationException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -70,17 +63,14 @@ public void run() { } } }); - - - while (true) { - AsynchronousSocketChannel clientChannel = srvChannel.accept().get(); - - ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); - - clientChannel.read(handler.getReceivingBuffer(), handler, - new RequestCompletionHandler(clientChannel)); - } - + + Thread srvThread = new Thread(new ServerProcess(new ConcreteTaskHandler[] { + + })); + srvThread.setDaemon(true); + srvThread.start(); + + REPL.startRepl(); } } diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index ca366ce..44faac4 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -4,6 +4,8 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; +import java.util.Set; + import torrent.common.StorageManager; public class PieceDownloader implements CompletionHandler { @@ -11,25 +13,26 @@ public class PieceDownloader implements CompletionHandler= file_length ? piece_size - : (pieceIdx + 1) * piece_size - file_length; - - buffer = ByteBuffer.wrap(filesDownloader.stm.data.files.get(fileId), pieceOffset, pieceLength); + : (pieceIdx + 1) * piece_size - file_length; + + buffer = ByteBuffer.wrap(filesDownloader.filesHolder.files.get(fileId), pieceOffset, pieceLength); } - + public ByteBuffer getBuffer() { return buffer; } @@ -39,7 +42,7 @@ public void completed(Integer result, AsynchronousSocketChannel attachment) { if (buffer.hasRemaining()) { attachment.read(buffer, attachment, this); } else { - filesDownloader.stm.data.completePieces.get(fileId).add(pieceIdx); + filesDownloader.filesHolder.completePieces.get(fileId).add(pieceIdx); try { attachment.close(); } catch (IOException e) { @@ -52,8 +55,8 @@ public void completed(Integer result, AsynchronousSocketChannel attachment) { @Override public void failed(Throwable exc, AsynchronousSocketChannel attachment) { // TODO Auto-generated method stub - + } - + } diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java new file mode 100644 index 0000000..579ffb9 --- /dev/null +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -0,0 +1,151 @@ +package torrent.client; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.net.SocketAddress; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +public class REPL { + + private final FilesHolder filesHolder; + private final FilesDownloader downloader; + + private Map listedFilesSize = new HashMap<>(); + private Map listedFilesName = new HashMap<>(); + + private SocketAddress toServer; + PrintStream out = System.out; + + public REPL(FilesHolder filesHolder, FilesDownloader downloader, SocketAddress toServer) { + this.filesHolder = filesHolder; + this.downloader = downloader; + this.toServer = toServer; + } + + void printStatus() { + + for (Integer id : filesHolder.files.keySet()) { + out.print(id + " " + filesHolder.filePaths.get(id) + " "); + if (filesHolder.completedFiles.contains(id)) { + out.println("complete"); + continue; + } + if (downloader.fileDownloadsFutures.containsKey(id)) { + out.print("->"); + } else { + out.print("||"); + } + out.println(filesHolder.completePieces.get(id).size() + " / " + + filesHolder.numParts(id)); + } + } + + void startDownload(int id, String filename) throws FileNotFoundException, FileProblemException, IOException { + Long size = listedFilesSize.get(id); + if (size == null) { + out.println("Unknown file size. Make list first."); + return; + } + filesHolder.addFileToDownload(id, size, filename); + downloader.startFileDownload(id); + } + + void deleteFile(int id) { + downloader.stopFileDownload(id); + filesHolder.deleteFile(id); + } + + void publishFile(String pathTo) { + try { + int id = Uploader.uploadAndGetId(toServer, pathTo); + if (id == 1) { + out.println("file not exist"); + return; + } + + filesHolder.addExistingFile(id, Paths.get(pathTo)); + } catch (IOException e) { + out.println(e.getMessage()); + } catch (FileProblemException e) { + out.println(e.getMessage()); + } + } + + void listAvaliableFiles() { + try { + ServerFilesLister.list(toServer, listedFilesSize, listedFilesName); + + for (int id : listedFilesName.keySet()) { + out.print(id + " " + listedFilesName.get(id)); + if (filesHolder.files.containsKey(id)) { + if (filesHolder.completedFiles.contains(id)) { + out.println("downloaded"); + } else if (downloader.fileDownloadsFutures.containsKey(id)) + } + } + + } catch (IOException e) { + out.println("Failed to list avaliable files"); + out.println(e.getMessage()); + } + } + + public void startRepl() { + /* + Options options = new Options(); + options.addOption("list", "list files, known to server"); + options.addOption("publish", true, "make file known by torrent"); + options.addOption("delete", true, "make file unknown by your client"); + options.addOption("get", true, "download file by id"); + options.addOption("pause", true, "stop file download"); + options.addOption("resume", true, "resume file downloading"); + options.addOption("status", true, "list files known by your client and their status"); + */ + + try (Scanner in = new Scanner(System.in)) { + while (true) { + try { + System.out.print(">"); + switch (in.next()) { + case "list": + { + + } + break; + case "publish": + { + publishFile(in.nextLine()); + } + break; + case "delete": + { + deleteFile(in.nextInt()); + } + break; + case "get": + { + startDownload(in.nextInt(), in.next()); + } + break; + case "pause": + { + downloader.stopFileDownload(in.nextInt()); + } + break; + case "status": + { + printStatus(); + } + break; + } + } catch (Exception e) { + out.println(e.getMessage()); + } + } + } + } +} diff --git a/torrent/src/main/java/torrent/client/ServerFilesLister.java b/torrent/src/main/java/torrent/client/ServerFilesLister.java new file mode 100644 index 0000000..fd02771 --- /dev/null +++ b/torrent/src/main/java/torrent/client/ServerFilesLister.java @@ -0,0 +1,35 @@ +package torrent.client; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.Map; + +public class ServerFilesLister { + public static void list(SocketAddress addr, Map fileSizes, Map filesNames) throws IOException { + try ( + Socket s = new Socket(); + DataOutputStream dout = new DataOutputStream(s.getOutputStream()); + DataInputStream dinp = new DataInputStream(s.getInputStream())) + { + s.connect(addr); + dout.writeByte(1); + + fileSizes.clear(); + filesNames.clear(); + + int count = dinp.readInt(); + + for (int i = 0; i < count; i++) { + int id = dinp.readInt(); + String name = dinp.readUTF(); + long size = dinp.readLong(); + + fileSizes.put(id, size); + filesNames.put(id, name); + } + } + } +} diff --git a/torrent/src/main/java/torrent/client/ServerProcess.java b/torrent/src/main/java/torrent/client/ServerProcess.java new file mode 100644 index 0000000..7011dc4 --- /dev/null +++ b/torrent/src/main/java/torrent/client/ServerProcess.java @@ -0,0 +1,47 @@ +package torrent.client; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.ExecutionException; + +import torrent.common.ConcreteTaskHandler; +import torrent.common.RequestCompletionHandler; +import torrent.common.ServerRequestHandler; + +public class ServerProcess implements Runnable { + + final static int myPort = 8082; + + private final AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); + + private final ConcreteTaskHandler[] concreteHandlers; + + ServerProcess(ConcreteTaskHandler[] concreteHandlers) throws IOException { + this.concreteHandlers = concreteHandlers; + srvChannel.bind(new InetSocketAddress(myPort)); + } + + @Override + public void run() { + while (true) { + AsynchronousSocketChannel clientChannel = null; + try { + clientChannel = srvChannel.accept().get(); + } catch (InterruptedException e) { + return; + } catch (ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); + + clientChannel.read(handler.getReceivingBuffer(), handler, + new RequestCompletionHandler(clientChannel)); + } + + } + +} diff --git a/torrent/src/main/java/torrent/client/SingleFileDownloader.java b/torrent/src/main/java/torrent/client/SingleFileDownloader.java index b9aaaa2..2bc9bb4 100644 --- a/torrent/src/main/java/torrent/client/SingleFileDownloader.java +++ b/torrent/src/main/java/torrent/client/SingleFileDownloader.java @@ -14,29 +14,27 @@ import java.util.Map; import java.util.Random; -import torrent.common.StorageManager; +public class SingleFileDownloader implements Runnable { -public class SingleFileDownloader implements Runnable{ - private SocketAddress srvAddr; - final StorageManager stm; + final FilesHolder filesHolder; private final int fileId; private final int numPieces; private Random rand = new Random(3); - + private List fileSources = new ArrayList<>(); private Map> pieceSources = new HashMap<>(); private Map pieceChannels = new HashMap<>(); - - public SingleFileDownloader(SocketAddress srvAddr, StorageManager stm, int fileId) { + + public SingleFileDownloader(SocketAddress srvAddr, FilesHolder stm, int fileId) { this.srvAddr = srvAddr; - this.stm = stm; + this.filesHolder = stm; this.fileId = fileId; - - numPieces = stm.data.numParts(fileId); + + numPieces = stm.numParts(fileId); } - + private void updateFileSources() { DataOutputStream out; DataInputStream in; @@ -44,41 +42,41 @@ private void updateFileSources() { toServer.connect(srvAddr); out = new DataOutputStream(toServer.getOutputStream()); in = new DataInputStream(toServer.getInputStream()); - out.writeByte(3); - out.writeInt(fileId); - - int clientsCount = in.readInt(); - fileSources.clear(); - byte[] ipBuf = new byte[4]; - for (int i = 0; i < clientsCount; i++) { - in.readFully(ipBuf); - - fileSources.add(new InetSocketAddress(InetAddress.getByAddress(ipBuf), - in.readShort())); - } + out.writeByte(3); + out.writeInt(fileId); + + int clientsCount = in.readInt(); + fileSources.clear(); + byte[] ipBuf = new byte[4]; + for (int i = 0; i < clientsCount; i++) { + in.readFully(ipBuf); + + fileSources.add(new InetSocketAddress(InetAddress.getByAddress(ipBuf), + in.readShort())); + } } catch (IOException e) { System.out.println("SourceUpdater: creation of output stream failed while update file sources"); } } - + private void updatePieceSources() { DataOutputStream out; DataInputStream in; - + pieceSources.clear(); for (int i = 0; i < numPieces; i++) { pieceSources.put(i, new ArrayList<>()); } - + for (SocketAddress addr : fileSources) { try (Socket othClient = new Socket()) { othClient.connect(addr, 10000); out = new DataOutputStream(othClient.getOutputStream()); in = new DataInputStream(othClient.getInputStream()); - + out.writeByte(1); out.writeInt(fileId); - + int partCount = in.readInt(); for (int i = 0; i < partCount; i++) { @@ -91,10 +89,10 @@ private void updatePieceSources() { } } } - + private void dispatchPieceDownloaders() { for (int i = 0; i < numPieces; i++) { - if (stm.data.completePieces.get(fileId).contains(i)) { + if (filesHolder.completePieces.get(fileId).contains(i)) { pieceChannels.remove(i); continue; } @@ -105,11 +103,11 @@ private void dispatchPieceDownloaders() { pieceChannels.remove(i); } } - + List sources = pieceSources.get(i); if (!sources.isEmpty()) { int rIdx = rand.nextInt(sources.size()); - + try { chan = AsynchronousSocketChannel.open(); } catch (IOException e) { @@ -124,20 +122,50 @@ private void dispatchPieceDownloaders() { } } + private boolean checkIfComplete() { + if (filesHolder.completePieces.get(fileId).size() < filesHolder.numParts(fileId)) { + return false; + } + try { + filesHolder.completedFiles.add(fileId); + filesHolder.save(); + } catch (IOException e){ + e.printStackTrace(); + } + return true; + } + + private void closeAllChannels() { + pieceChannels.forEach((i, ch) -> { + try { + ch.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + }); + } + @Override - public void run() { + public void run() { while (true) { - try { - Thread.sleep(10000); - } catch (InterruptedException e) { + if (checkIfComplete()) { + closeAllChannels(); return; } updateFileSources(); updatePieceSources(); dispatchPieceDownloaders(); - if (stm.data.completedFiles.contains(fileId)) { + + if (filesHolder.completedFiles.contains(fileId)) { break; } + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + closeAllChannels(); + return; + } } } } diff --git a/torrent/src/main/java/torrent/client/UpdatePerformer.java b/torrent/src/main/java/torrent/client/UpdatePerformer.java index e419a2b..7778d23 100644 --- a/torrent/src/main/java/torrent/client/UpdatePerformer.java +++ b/torrent/src/main/java/torrent/client/UpdatePerformer.java @@ -9,11 +9,11 @@ public class UpdatePerformer implements Runnable { private Socket toServer; - private StorageManager stm; + private FilesHolder filesHolder; private short myPort; - public UpdatePerformer(StorageManager stm, Socket toServer, short myPort) { - this.stm = stm; + public UpdatePerformer(FilesHolder filesHolder, Socket toServer, short myPort) { + this.filesHolder = filesHolder; this.toServer = toServer; this.myPort = myPort; } @@ -22,12 +22,7 @@ public UpdatePerformer(StorageManager stm, Socket toServer, short myPort) { public void run() { while(true) { - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - return; - } - + DataOutputStream out; try { out = new DataOutputStream(toServer.getOutputStream()); @@ -38,17 +33,20 @@ public void run() { continue; } - stm.lock.readLock().lock(); try { - out.writeInt(stm.data.completePieces.size()); - for (Integer fileId : stm.data.completePieces.keySet()) { + out.writeInt(filesHolder.completePieces.size()); + for (Integer fileId : filesHolder.completePieces.keySet()) { out.writeInt(fileId); } } catch (IOException e) { System.out.println("UpdatePerformer failed"); continue; - } finally { - stm.lock.readLock().unlock(); + } + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + return; } } } diff --git a/torrent/src/main/java/torrent/client/Uploader.java b/torrent/src/main/java/torrent/client/Uploader.java new file mode 100644 index 0000000..da5127b --- /dev/null +++ b/torrent/src/main/java/torrent/client/Uploader.java @@ -0,0 +1,30 @@ +package torrent.client; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class Uploader { + public static int uploadAndGetId(SocketAddress addr, String filePath) throws IOException { + try ( + Socket s = new Socket(); + DataOutputStream dout = new DataOutputStream(s.getOutputStream()); + DataInputStream dinp = new DataInputStream(s.getInputStream()) + ) { + s.connect(addr, 10000); + if (!Files.isRegularFile(Paths.get(filePath))) { + return -1; + } + long len = Paths.get(filePath).toFile().length(); + dout.writeByte(2); + dout.writeUTF(Paths.get(filePath).getFileName().toString()); + dout.writeLong(len); + + return dinp.readInt(); + } + } +} From 44ce551735c8e124e6d943d488d9066fac694433 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 15 Dec 2018 19:25:35 +0300 Subject: [PATCH 05/25] =?UTF-8?q?=D0=92=D0=B5=D1=81=D1=8C=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/torrent/client/FilesDownloader.java | 13 +++- .../main/java/torrent/client/FilesHolder.java | 45 +++++++++----- .../main/java/torrent/client/GetHandler.java | 43 +++++++++++++ .../src/main/java/torrent/client/Main.java | 25 +++----- .../java/torrent/client/PieceDownloader.java | 12 +--- .../src/main/java/torrent/client/REPL.java | 57 +++++++++++++---- .../torrent/client/SingleFileDownloader.java | 35 ++++++----- .../main/java/torrent/client/StatHandler.java | 47 ++++++++++++++ .../java/torrent/client/UpdatePerformer.java | 2 - .../torrent/common/ConcreteTaskHandler.java | 2 - .../torrent/common/ServerRequestHandler.java | 62 ++++++++++--------- .../AbstractServerTaskHandler.java} | 17 +++-- .../main/java/torrent/server/ListHandler.java | 25 +++----- .../src/main/java/torrent/server/Main.java | 5 +- .../torrent/server/OldClientsCleaner.java | 26 ++++---- .../main/java/torrent/server/ServerData.java | 6 +- .../java/torrent/server/SourcesHandler.java | 17 ++--- .../{common => server}/StorageManager.java | 32 +++++----- .../java/torrent/server/UpdateHandler.java | 9 +-- .../java/torrent/server/UploadHandler.java | 10 +-- 20 files changed, 294 insertions(+), 196 deletions(-) create mode 100644 torrent/src/main/java/torrent/client/GetHandler.java create mode 100644 torrent/src/main/java/torrent/client/StatHandler.java rename torrent/src/main/java/torrent/{common/AbstractConcreteTaskHandler.java => server/AbstractServerTaskHandler.java} (53%) rename torrent/src/main/java/torrent/{common => server}/StorageManager.java (56%) diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index f31440b..835fa33 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -7,6 +7,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import torrent.client.FilesHolder.FileStatus; + public class FilesDownloader { FilesHolder filesHolder; @@ -22,21 +24,26 @@ public FilesDownloader(FilesHolder stm, SocketAddress toServer) { } public boolean startFileDownload(int fileId) { - if (fileDownloadsFutures.containsKey(fileId)) { + if (filesHolder.fileStatus.get(fileId) != FileStatus.Paused) { return false; } + + filesHolder.fileStatus.put(fileId, FileStatus.Downloading); + SingleFileDownloader downloader = new SingleFileDownloader(toServer, filesHolder, fileId); - //fileDownloads.put(fileId, downloader); fileDownloadsFutures.put(fileId, pool.submit(downloader)); return true; } public void stopFileDownload(int fileId) { - if (!fileDownloadsFutures.containsKey(fileId)) { + 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 index a88fd81..77b6583 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -23,33 +23,46 @@ public class FilesHolder { public final int pieceSize = 0xA00000; - //public final ReadWriteLock lock = new ReentrantReadWriteLock(); //рабочие данные + + public enum FileStatus {Complete, Downloading, Paused}; + public Map files = new ConcurrentHashMap<>(); public Map filePaths = new ConcurrentHashMap<>(); - public Set completedFiles = ConcurrentHashMap.newKeySet(); + public Map fileStatus = new ConcurrentHashMap<>(); public Map> completePieces = new ConcurrentHashMap<>(); // ObjectMapper mapper = new ObjectMapper(); private final Path mapPath = Paths.get("torrentData"); - private final Path filesPaths = mapPath.resolve("filepaths"); - private final Path compFiles = mapPath.resolve("completeFiles"); - private final Path comPieces = mapPath.resolve("completePieces"); + private final Path filePathsPath = mapPath.resolve("filepaths"); + private final Path fileStatusPath = mapPath.resolve("filesStatus"); + private final Path comletePiecesPath = mapPath.resolve("completePieces"); public int numParts(int fileId) { return (files.get(fileId).length + pieceSize - 1) / pieceSize; } + + public int pieceOffset(int fileId, int numPart) { + return pieceSize * numPart; + } + + public int pieceLenght(int fileId, int numPart) { + int file_length = files.get(fileId).length; + return (numPart + 1) * pieceSize >= file_length + ? pieceSize + : (numPart + 1) * pieceSize - file_length; + } public FilesHolder() throws JsonGenerationException, JsonMappingException, IOException { load(); } public void save() throws JsonGenerationException, JsonMappingException, IOException { - mapper.writeValue(filesPaths.toFile(), filePaths); - mapper.writeValue(compFiles.toFile(), completedFiles); - mapper.writeValue(comPieces.toFile(), completePieces); + mapper.writeValue(filePathsPath.toFile(), filePaths); + mapper.writeValue(fileStatusPath.toFile(), fileStatus); + mapper.writeValue(comletePiecesPath.toFile(), completePieces); for (Entry ent : filePaths.entrySet()) { try (FileOutputStream fout = new FileOutputStream(new File(ent.getValue()))) { @@ -59,9 +72,9 @@ public void save() throws JsonGenerationException, JsonMappingException, IOExcep } public void save(int fileId) throws FileNotFoundException, IOException { - mapper.writeValue(filesPaths.toFile(), filePaths); - mapper.writeValue(compFiles.toFile(), completedFiles); - mapper.writeValue(comPieces.toFile(), completePieces); + mapper.writeValue(filePathsPath.toFile(), filePaths); + mapper.writeValue(fileStatusPath.toFile(), fileStatus); + mapper.writeValue(comletePiecesPath.toFile(), completePieces); if (files.containsKey(fileId)) { try (FileOutputStream fout = new FileOutputStream(new File(filePaths.get(fileId)))) { @@ -71,9 +84,9 @@ public void save(int fileId) throws FileNotFoundException, IOException { } public void load() throws JsonGenerationException, JsonMappingException, IOException { - filePaths = mapper.readValue(filesPaths.toFile(), filePaths.getClass()); - completedFiles = mapper.readValue(compFiles.toFile(), completedFiles.getClass()); - completePieces = mapper.readValue(comPieces.toFile(), completePieces.getClass()); + filePaths = mapper.readValue(filePathsPath.toFile(), filePaths.getClass()); + fileStatus = mapper.readValue(fileStatusPath.toFile(), fileStatus.getClass()); + completePieces = mapper.readValue(comletePiecesPath.toFile(), completePieces.getClass()); for (Entry ent : filePaths.entrySet()) { try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { @@ -85,7 +98,7 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep public void deleteFile(int id) { files.remove(id); filePaths.remove(id); - completedFiles.remove(id); + fileStatus.remove(id); completePieces.remove(id); } @@ -114,7 +127,7 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File files.put(id, finp.readAllBytes()); } filePaths.put(id, path.toString()); - completedFiles.add(id); + fileStatus.put(id, FileStatus.Complete); save(-1); } diff --git a/torrent/src/main/java/torrent/client/GetHandler.java b/torrent/src/main/java/torrent/client/GetHandler.java new file mode 100644 index 0000000..2379804 --- /dev/null +++ b/torrent/src/main/java/torrent/client/GetHandler.java @@ -0,0 +1,43 @@ +package torrent.client; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; + +import torrent.common.ConcreteTaskHandler; +import torrent.common.ServerRequestHandler.MessageProcessStatus; + +public class GetHandler implements ConcreteTaskHandler { + + private final FilesHolder fHolder; + + GetHandler(FilesHolder fHolder) { + this.fHolder = fHolder; + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { + try { + int id = in.readInt(); + int partNum = in.readInt(); + + if (partNum >= fHolder.numParts(id)) { + return MessageProcessStatus.ERROR; + } + + out.write( + fHolder.files.get(id), + fHolder.pieceOffset(id, partNum), + fHolder.pieceLenght(id, partNum)); + + return MessageProcessStatus.SUCCESS; + } catch (EOFException e) { + return MessageProcessStatus.INCOMPLETE; + } catch (IOException e) { + return MessageProcessStatus.ERROR; + } + } + +} diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java index b03ddac..da5628d 100644 --- a/torrent/src/main/java/torrent/client/Main.java +++ b/torrent/src/main/java/torrent/client/Main.java @@ -21,7 +21,6 @@ import torrent.common.ConcreteTaskHandler; import torrent.common.RequestCompletionHandler; import torrent.common.ServerRequestHandler; -import torrent.common.StorageManager; import torrent.server.ListHandler; import torrent.server.OldClientsCleaner; import torrent.server.ServerData; @@ -31,26 +30,19 @@ public class Main { - final SocketAddress toServer = new InetSocketAddress("localhost", 8081); - - final static String helpMessage = - "This is torrent client"; - - public Main() throws IOException { - - } public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException, InterruptedException, ExecutionException { + + final SocketAddress toServer = new InetSocketAddress(args[0], 8081); - FilesHolder storageManager = new FilesHolder(); - + FilesHolder filesHolder = new FilesHolder(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Saving client state..."); try { - storageManager.save(); + filesHolder.save(); } catch (JsonGenerationException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -65,12 +57,15 @@ public void run() { }); Thread srvThread = new Thread(new ServerProcess(new ConcreteTaskHandler[] { - + new StatHandler(filesHolder), + new GetHandler(filesHolder) })); + srvThread.setDaemon(true); srvThread.start(); - - REPL.startRepl(); + + REPL repl = new REPL(filesHolder, new FilesDownloader(filesHolder, toServer), toServer); + repl.startRepl(); } } diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index 44faac4..b8521da 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -6,8 +6,6 @@ import java.nio.channels.CompletionHandler; import java.util.Set; -import torrent.common.StorageManager; - public class PieceDownloader implements CompletionHandler { private int fileId; private int pieceOffset, pieceLength; @@ -21,14 +19,10 @@ public PieceDownloader(SingleFileDownloader filesDownloader, int fileId, int pie this.fileId = fileId; this.pieceIdx = pieceIdx; - int piece_size = filesDownloader.filesHolder.pieceSize; - - int file_length = filesDownloader.filesHolder.files.get(fileId).length; + filesDownloader.filesHolder.files.get(fileId); - this.pieceOffset = pieceIdx * piece_size; - this.pieceLength = (pieceIdx + 1) * piece_size >= file_length - ? piece_size - : (pieceIdx + 1) * piece_size - file_length; + this.pieceOffset = filesDownloader.filesHolder.pieceOffset(fileId, pieceIdx); + this.pieceLength = filesDownloader.filesHolder.pieceLenght(fileId, pieceIdx); buffer = ByteBuffer.wrap(filesDownloader.filesHolder.files.get(fileId), pieceOffset, pieceLength); } diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index 579ffb9..5b71dee 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -30,14 +30,22 @@ void printStatus() { for (Integer id : filesHolder.files.keySet()) { out.print(id + " " + filesHolder.filePaths.get(id) + " "); - if (filesHolder.completedFiles.contains(id)) { + switch (filesHolder.fileStatus.get(id)) { + case Complete: + { out.println("complete"); continue; } - if (downloader.fileDownloadsFutures.containsKey(id)) { + case Downloading: + { out.print("->"); - } else { + break; + } + case Paused: + { out.print("||"); + break; + } } out.println(filesHolder.completePieces.get(id).size() + " / " + filesHolder.numParts(id)); @@ -74,26 +82,49 @@ void publishFile(String pathTo) { out.println(e.getMessage()); } } - + void listAvaliableFiles() { try { ServerFilesLister.list(toServer, listedFilesSize, listedFilesName); - + for (int id : listedFilesName.keySet()) { out.print(id + " " + listedFilesName.get(id)); - if (filesHolder.files.containsKey(id)) { - if (filesHolder.completedFiles.contains(id)) { - out.println("downloaded"); - } else if (downloader.fileDownloadsFutures.containsKey(id)) + switch (filesHolder.fileStatus.get(id)) { + case Complete: + { + out.println("complete"); + continue; + } + case Downloading: + { + out.print("->"); + break; + } + case Paused: + { + out.print("||"); + break; + } } } - + } catch (IOException e) { out.println("Failed to list avaliable files"); out.println(e.getMessage()); } } + final String helpMessage = + "This is torrent client.\n" + + "commands:\n" + + "list\n" + + "publish \n" + + "delete \n" + + "get \n" + + "pause \n" + + "status\n" + + "help"; + public void startRepl() { /* Options options = new Options(); @@ -111,9 +142,13 @@ public void startRepl() { try { System.out.print(">"); switch (in.next()) { + case "help": + { + out.println(helpMessage); + } case "list": { - + listAvaliableFiles(); } break; case "publish": diff --git a/torrent/src/main/java/torrent/client/SingleFileDownloader.java b/torrent/src/main/java/torrent/client/SingleFileDownloader.java index 2bc9bb4..63a0097 100644 --- a/torrent/src/main/java/torrent/client/SingleFileDownloader.java +++ b/torrent/src/main/java/torrent/client/SingleFileDownloader.java @@ -14,6 +14,8 @@ import java.util.Map; import java.util.Random; +import torrent.client.FilesHolder.FileStatus; + public class SingleFileDownloader implements Runnable { private SocketAddress srvAddr; @@ -127,7 +129,7 @@ private boolean checkIfComplete() { return false; } try { - filesHolder.completedFiles.add(fileId); + filesHolder.fileStatus.put(fileId, FileStatus.Complete); filesHolder.save(); } catch (IOException e){ e.printStackTrace(); @@ -147,25 +149,24 @@ private void closeAllChannels() { @Override public void run() { - while (true) { - if (checkIfComplete()) { - closeAllChannels(); - return; - } - updateFileSources(); - updatePieceSources(); - dispatchPieceDownloaders(); + try { + while (true) { + if (checkIfComplete()) { + return; + } - if (filesHolder.completedFiles.contains(fileId)) { - break; - } + updateFileSources(); + updatePieceSources(); + dispatchPieceDownloaders(); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - closeAllChannels(); - return; + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + return; + } } + } finally { + closeAllChannels(); } } } diff --git a/torrent/src/main/java/torrent/client/StatHandler.java b/torrent/src/main/java/torrent/client/StatHandler.java new file mode 100644 index 0000000..baf4305 --- /dev/null +++ b/torrent/src/main/java/torrent/client/StatHandler.java @@ -0,0 +1,47 @@ +package torrent.client; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Set; + +import torrent.common.ConcreteTaskHandler; +import torrent.common.ServerRequestHandler.MessageProcessStatus; + +public class StatHandler implements ConcreteTaskHandler { + + private final FilesHolder fHolder; + + StatHandler(FilesHolder fHolder) { + this.fHolder = fHolder; + } + + @Override + public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { + try { + int id = in.readInt(); + + Set pieces = fHolder.completePieces.get(id); + + if (pieces == null) { + return MessageProcessStatus.ERROR; + } + + out.writeInt(pieces.size()); + + for (int i : pieces) { + out.write(i); + } + + return MessageProcessStatus.SUCCESS; + } catch (EOFException e) { + return MessageProcessStatus.INCOMPLETE; + } catch (IOException e) { + return MessageProcessStatus.ERROR; + } + + } + +} diff --git a/torrent/src/main/java/torrent/client/UpdatePerformer.java b/torrent/src/main/java/torrent/client/UpdatePerformer.java index 7778d23..d50892d 100644 --- a/torrent/src/main/java/torrent/client/UpdatePerformer.java +++ b/torrent/src/main/java/torrent/client/UpdatePerformer.java @@ -4,8 +4,6 @@ import java.io.IOException; import java.net.Socket; -import torrent.common.StorageManager; - public class UpdatePerformer implements Runnable { private Socket toServer; diff --git a/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java b/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java index db48e73..4514a21 100644 --- a/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java +++ b/torrent/src/main/java/torrent/common/ConcreteTaskHandler.java @@ -3,8 +3,6 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.InetSocketAddress; -import java.net.SocketAddress; - import torrent.common.ServerRequestHandler.MessageProcessStatus; public interface ConcreteTaskHandler { diff --git a/torrent/src/main/java/torrent/common/ServerRequestHandler.java b/torrent/src/main/java/torrent/common/ServerRequestHandler.java index f839ea5..a19b8bc 100644 --- a/torrent/src/main/java/torrent/common/ServerRequestHandler.java +++ b/torrent/src/main/java/torrent/common/ServerRequestHandler.java @@ -9,50 +9,52 @@ import java.nio.ByteBuffer; public class ServerRequestHandler{ - + private ByteBuffer inputBuffer = ByteBuffer.allocate(1000); private ByteBuffer outputBuffer; - + public enum MessageProcessStatus {INCOMPLETE, ERROR, SUCCESS}; - + private ConcreteTaskHandler[] handlers; - + public ServerRequestHandler(ConcreteTaskHandler[] handlers) { this.handlers = handlers; } - + public ByteBuffer getReceivingBuffer() { inputBuffer.clear(); return inputBuffer; } - public MessageProcessStatus messageProcessAttemp(InetSocketAddress clientInf) { - DataInputStream dInp = new DataInputStream( - new ByteArrayInputStream(inputBuffer.array(), - inputBuffer.arrayOffset(), - inputBuffer.limit() - inputBuffer.position())); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dOut = new DataOutputStream(bout); - - byte typeIndex; - try { - typeIndex = dInp.readByte(); - } catch (IOException e) { - return MessageProcessStatus.INCOMPLETE; - } - - if (typeIndex > handlers.length) { - return MessageProcessStatus.ERROR; - } - - MessageProcessStatus status = handlers[typeIndex - 1].computeResult(dInp, dOut, clientInf); - if (status != MessageProcessStatus.SUCCESS) { + public MessageProcessStatus messageProcessAttemp(InetSocketAddress clientInf) throws IOException { + try ( + DataInputStream dInp = new DataInputStream( + new ByteArrayInputStream(inputBuffer.array(), + inputBuffer.arrayOffset(), + inputBuffer.limit() - inputBuffer.position())); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dOut = new DataOutputStream(bout); + ) { + byte typeIndex; + try { + typeIndex = dInp.readByte(); + } catch (IOException e) { + return MessageProcessStatus.INCOMPLETE; + } + + if (typeIndex > handlers.length) { + return MessageProcessStatus.ERROR; + } + + MessageProcessStatus status = handlers[typeIndex - 1].computeResult(dInp, dOut, clientInf); + if (status != MessageProcessStatus.SUCCESS) { + return status; + } + + outputBuffer = ByteBuffer.wrap(bout.toByteArray()); return status; } - - outputBuffer = ByteBuffer.wrap(bout.toByteArray()); - return status; } public ByteBuffer getTransmittingBuffer() { diff --git a/torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java b/torrent/src/main/java/torrent/server/AbstractServerTaskHandler.java similarity index 53% rename from torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java rename to torrent/src/main/java/torrent/server/AbstractServerTaskHandler.java index a6a37ba..83e6e52 100644 --- a/torrent/src/main/java/torrent/common/AbstractConcreteTaskHandler.java +++ b/torrent/src/main/java/torrent/server/AbstractServerTaskHandler.java @@ -1,19 +1,18 @@ -package torrent.common; +package torrent.server; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.InetSocketAddress; -import java.net.SocketAddress; - +import torrent.common.ConcreteTaskHandler; import torrent.common.ServerRequestHandler.MessageProcessStatus; -public abstract class AbstractConcreteTaskHandler implements ConcreteTaskHandler{ - protected final StorageManager storage; - - public AbstractConcreteTaskHandler(StorageManager stm) { - storage = stm; +public abstract class AbstractServerTaskHandler implements ConcreteTaskHandler{ + protected final StorageManager storage; + + public AbstractServerTaskHandler(StorageManager storage) { + this.storage = storage; } - + @Override public abstract MessageProcessStatus computeResult( DataInputStream in, diff --git a/torrent/src/main/java/torrent/server/ListHandler.java b/torrent/src/main/java/torrent/server/ListHandler.java index 59a7eda..3528b1e 100644 --- a/torrent/src/main/java/torrent/server/ListHandler.java +++ b/torrent/src/main/java/torrent/server/ListHandler.java @@ -4,34 +4,27 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.SocketAddress; - -import torrent.common.AbstractConcreteTaskHandler; import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; -import torrent.common.StorageManager; -public class ListHandler extends AbstractConcreteTaskHandler { +public class ListHandler extends AbstractServerTaskHandler { - public ListHandler(StorageManager stm) { + public ListHandler(StorageManager stm) { super(stm); } @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf){ - storage.lock.readLock().lock(); try { - out.writeInt(storage.data.map.size()); - - for(FileInformation d : storage.data.map.values()) { - out.writeInt(d.id); - out.writeUTF(d.name); - out.writeLong(d.size); - } + out.writeInt(storage.data.map.size()); + + for(FileInformation d : storage.data.map.values()) { + out.writeInt(d.id); + out.writeUTF(d.name); + out.writeLong(d.size); + } } catch (IOException e) { return MessageProcessStatus.ERROR; - } finally { - storage.lock.readLock().unlock(); } return MessageProcessStatus.SUCCESS; diff --git a/torrent/src/main/java/torrent/server/Main.java b/torrent/src/main/java/torrent/server/Main.java index 5e427cf..37a92f8 100644 --- a/torrent/src/main/java/torrent/server/Main.java +++ b/torrent/src/main/java/torrent/server/Main.java @@ -13,11 +13,10 @@ import torrent.common.ConcreteTaskHandler; import torrent.common.RequestCompletionHandler; import torrent.common.ServerRequestHandler; -import torrent.common.StorageManager; public class Main { public static void main(String[] args) throws IOException, InterruptedException, ExecutionException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - StorageManager storageManager = new StorageManager<>(ServerData.class, "serverFile"); + StorageManager storageManager = new StorageManager("serverFile"); AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); srvChannel.bind(new InetSocketAddress(8081)); @@ -37,9 +36,7 @@ public void run() { cleanThread.interrupt(); System.out.println("Saving server state..."); try { - storageManager.lock.writeLock().lock(); storageManager.save(); - storageManager.lock.writeLock().unlock(); } catch (JsonGenerationException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/torrent/src/main/java/torrent/server/OldClientsCleaner.java b/torrent/src/main/java/torrent/server/OldClientsCleaner.java index 877cec5..68c933b 100644 --- a/torrent/src/main/java/torrent/server/OldClientsCleaner.java +++ b/torrent/src/main/java/torrent/server/OldClientsCleaner.java @@ -1,14 +1,12 @@ package torrent.server; -import torrent.common.StorageManager; - public class OldClientsCleaner implements Runnable { - + public final long updateTime = 5 * 60 * 1000; - - private StorageManager storage; - - public OldClientsCleaner(StorageManager storage) { + + private StorageManager storage; + + public OldClientsCleaner(StorageManager storage) { this.storage = storage; } @@ -20,24 +18,22 @@ public void run() { } catch (InterruptedException e) { return; } - - storage.lock.writeLock().lock(); + long currTime = System.currentTimeMillis(); - + storage.data.lastClientUpdate .entrySet() .removeIf(ent -> currTime - ent.getValue() > updateTime); - + storage.data.map.values().stream().forEach(fInf -> - fInf.clients.removeIf(s -> !storage.data.lastClientUpdate.containsKey(s))); - + fInf.clients.removeIf(s -> !storage.data.lastClientUpdate.containsKey(s))); + try { storage.save(); } catch (Exception e) { System.out.println("Cleaner: error while saving. " + e.getMessage()); } - - storage.lock.writeLock().unlock(); + } } diff --git a/torrent/src/main/java/torrent/server/ServerData.java b/torrent/src/main/java/torrent/server/ServerData.java index 8684f21..5a932f4 100644 --- a/torrent/src/main/java/torrent/server/ServerData.java +++ b/torrent/src/main/java/torrent/server/ServerData.java @@ -1,13 +1,13 @@ package torrent.server; import java.net.InetSocketAddress; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import torrent.common.FileInformation; public class ServerData { public int filesCount = 0; - public Map map = new HashMap<>(); - public Map lastClientUpdate = new HashMap<>(); + public Map map = new ConcurrentHashMap<>(); + public Map lastClientUpdate = new ConcurrentHashMap<>(); } diff --git a/torrent/src/main/java/torrent/server/SourcesHandler.java b/torrent/src/main/java/torrent/server/SourcesHandler.java index 5729ccc..05eb190 100644 --- a/torrent/src/main/java/torrent/server/SourcesHandler.java +++ b/torrent/src/main/java/torrent/server/SourcesHandler.java @@ -5,34 +5,29 @@ import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.List; import java.util.Set; -import torrent.common.AbstractConcreteTaskHandler; import torrent.common.ServerRequestHandler.MessageProcessStatus; -import torrent.common.StorageManager; -public class SourcesHandler extends AbstractConcreteTaskHandler { +public class SourcesHandler extends AbstractServerTaskHandler { - public SourcesHandler(StorageManager stm) { + public SourcesHandler(StorageManager stm) { super(stm); } @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { - storage.lock.readLock().lock(); try { int id = in.readInt(); if (!storage.data.map.containsKey(id)) { return MessageProcessStatus.ERROR; } - + Set clients = storage.data.map.get(id).clients; out.writeInt(clients.size()); - + for (InetSocketAddress addr : storage.data.map.get(id).clients) { out.write(addr.getAddress().getAddress()); out.writeShort(addr.getPort()); @@ -42,10 +37,8 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o return MessageProcessStatus.INCOMPLETE; } catch (IOException e){ return MessageProcessStatus.ERROR; - } finally { - storage.lock.readLock().unlock(); } - + return MessageProcessStatus.SUCCESS; } diff --git a/torrent/src/main/java/torrent/common/StorageManager.java b/torrent/src/main/java/torrent/server/StorageManager.java similarity index 56% rename from torrent/src/main/java/torrent/common/StorageManager.java rename to torrent/src/main/java/torrent/server/StorageManager.java index 7f5c783..82a6e56 100644 --- a/torrent/src/main/java/torrent/common/StorageManager.java +++ b/torrent/src/main/java/torrent/server/StorageManager.java @@ -1,41 +1,39 @@ -package torrent.common; +package torrent.server; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.locks.ReentrantReadWriteLock; - import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -public class StorageManager { - +import torrent.server.ServerData; + +public class StorageManager { + private final Path savePath; private final ObjectMapper mapper = new ObjectMapper(); - - public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - - public volatile T data; - private final Class Tclass; - - public StorageManager(Class Tclass, String savePath) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + + //public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public volatile ServerData data; + + public StorageManager(String savePath) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { this.savePath = Paths.get(savePath); - this.Tclass = Tclass; try { load(); } catch (Exception e) { - data = Tclass.getConstructor().newInstance(); + data = new ServerData(); } } - + public void save() throws JsonGenerationException, JsonMappingException, IOException { mapper.writeValue(savePath.toFile(), data); } - + public void load() throws JsonParseException, JsonMappingException, IOException { - data = mapper.readValue(savePath.toFile(), Tclass); + data = mapper.readValue(savePath.toFile(), ServerData.class); } } diff --git a/torrent/src/main/java/torrent/server/UpdateHandler.java b/torrent/src/main/java/torrent/server/UpdateHandler.java index 2b83b50..69935a1 100644 --- a/torrent/src/main/java/torrent/server/UpdateHandler.java +++ b/torrent/src/main/java/torrent/server/UpdateHandler.java @@ -6,20 +6,17 @@ import java.io.IOException; import java.net.InetSocketAddress; -import torrent.common.AbstractConcreteTaskHandler; import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; -import torrent.common.StorageManager; -public class UpdateHandler extends AbstractConcreteTaskHandler { +public class UpdateHandler extends AbstractServerTaskHandler { - public UpdateHandler(StorageManager stm) { + public UpdateHandler(StorageManager stm) { super(stm); } @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { - storage.lock.writeLock().lock(); try { short clientPort = in.readShort(); int count = in.readInt(); @@ -46,8 +43,6 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o out.writeBoolean(false); } catch (IOException e1) {} return MessageProcessStatus.ERROR; - } finally { - storage.lock.writeLock().unlock(); } return MessageProcessStatus.SUCCESS; diff --git a/torrent/src/main/java/torrent/server/UploadHandler.java b/torrent/src/main/java/torrent/server/UploadHandler.java index 17e4205..e188e87 100644 --- a/torrent/src/main/java/torrent/server/UploadHandler.java +++ b/torrent/src/main/java/torrent/server/UploadHandler.java @@ -5,23 +5,19 @@ import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.util.Arrays; -import torrent.common.AbstractConcreteTaskHandler; import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; -import torrent.common.StorageManager; -public class UploadHandler extends AbstractConcreteTaskHandler { +public class UploadHandler extends AbstractServerTaskHandler { - public UploadHandler(StorageManager stm) { + public UploadHandler(StorageManager stm) { super(stm); } @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf) { - storage.lock.writeLock().lock(); try { String name = in.readUTF(); long size = in.readLong(); @@ -34,8 +30,6 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o return MessageProcessStatus.INCOMPLETE; } catch (IOException e){ return MessageProcessStatus.ERROR; - } finally { - storage.lock.writeLock().unlock(); } return MessageProcessStatus.SUCCESS; From 690260c038ae256df2ea4d629a0a97b2c66ce002 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 15 Dec 2018 22:18:15 +0300 Subject: [PATCH 06/25] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BD=20maven-assembly-plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 130 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/torrent/pom.xml b/torrent/pom.xml index 066da2d..f116666 100644 --- a/torrent/pom.xml +++ b/torrent/pom.xml @@ -1,51 +1,93 @@ - - 4.0.0 + + 4.0.0 - 1 - torrent - 0.0.1-SNAPSHOT - jar + 1 + torrent + 0.0.1-SNAPSHOT + - torrent - http://maven.apache.org + torrent + http://maven.apache.org - - UTF-8 - + + UTF-8 + - - - maven-compiler-plugin - - 1.9 - 1.9 - 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 + + torrentServer + false + + + + torrent_client + package + + single + + + + + + torrent.client.Main + + + + + jar-with-dependencies + + torrentClient + false + + + + + - - - junit - junit - 3.8.1 - test - + + + junit + junit + 3.8.1 + test + - - - com.fasterxml.jackson.core - jackson-databind - 2.9.7 - - - - - commons-cli - commons-cli - 1.4 - - - + + + com.fasterxml.jackson.core + jackson-databind + 2.9.7 + + + From 7f12c9057b69716c623c80c6bc1de8e7f2f8e2ab Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 16 Dec 2018 15:19:08 +0300 Subject: [PATCH 07/25] =?UTF-8?q?=D0=92=20main=20=D0=B7=D0=B0=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=20=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=BA,=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BB=D0=B5=D0=B3=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 4 +- .../torrent/client/FileProblemException.java | 5 ++ .../main/java/torrent/client/FilesHolder.java | 59 ++++++++++------ .../src/main/java/torrent/client/Main.java | 65 +++--------------- .../main/java/torrent/client/MainInner.java | 68 +++++++++++++++++++ .../java/torrent/client/PieceDownloader.java | 1 - .../src/main/java/torrent/client/REPL.java | 47 ++++++++----- .../torrent/client/ServerFilesLister.java | 17 +++-- .../java/torrent/client/UpdatePerformer.java | 36 ++++++---- .../main/java/torrent/client/Uploader.java | 17 +++-- .../{client => common}/ServerProcess.java | 11 ++- .../src/main/java/torrent/server/Main.java | 56 ++------------- .../main/java/torrent/server/MainInner.java | 46 +++++++++++++ .../java/torrent/server/StorageManager.java | 16 ++--- 14 files changed, 258 insertions(+), 190 deletions(-) create mode 100644 torrent/src/main/java/torrent/client/MainInner.java rename torrent/src/main/java/torrent/{client => common}/ServerProcess.java (79%) create mode 100644 torrent/src/main/java/torrent/server/MainInner.java diff --git a/torrent/pom.xml b/torrent/pom.xml index f116666..0313e2e 100644 --- a/torrent/pom.xml +++ b/torrent/pom.xml @@ -45,7 +45,7 @@ jar-with-dependencies - torrentServer + server false @@ -66,7 +66,7 @@ jar-with-dependencies - torrentClient + client false diff --git a/torrent/src/main/java/torrent/client/FileProblemException.java b/torrent/src/main/java/torrent/client/FileProblemException.java index f1a7b05..6cc213d 100644 --- a/torrent/src/main/java/torrent/client/FileProblemException.java +++ b/torrent/src/main/java/torrent/client/FileProblemException.java @@ -2,6 +2,11 @@ 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/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 77b6583..111b1e7 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -5,17 +5,13 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,9 +20,9 @@ public class FilesHolder { public final int pieceSize = 0xA00000; //рабочие данные - + public enum FileStatus {Complete, Downloading, Paused}; - + public Map files = new ConcurrentHashMap<>(); public Map filePaths = new ConcurrentHashMap<>(); @@ -43,11 +39,11 @@ public enum FileStatus {Complete, Downloading, Paused}; public int numParts(int fileId) { return (files.get(fileId).length + pieceSize - 1) / pieceSize; } - + public int pieceOffset(int fileId, int numPart) { return pieceSize * numPart; } - + public int pieceLenght(int fileId, int numPart) { int file_length = files.get(fileId).length; return (numPart + 1) * pieceSize >= file_length @@ -55,14 +51,26 @@ public int pieceLenght(int fileId, int numPart) { : (numPart + 1) * pieceSize - file_length; } - public FilesHolder() throws JsonGenerationException, JsonMappingException, IOException { + public FilesHolder() throws IOException { load(); } - - public void save() throws JsonGenerationException, JsonMappingException, IOException { + + private void writeMaps() throws IOException { + Files.createDirectory(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); + } + + public void save() throws JsonGenerationException, JsonMappingException, IOException { + + writeMaps(); for (Entry ent : filePaths.entrySet()) { try (FileOutputStream fout = new FileOutputStream(new File(ent.getValue()))) { @@ -70,12 +78,11 @@ public void save() throws JsonGenerationException, JsonMappingException, IOExcep } } } - + public void save(int fileId) throws FileNotFoundException, IOException { - mapper.writeValue(filePathsPath.toFile(), filePaths); - mapper.writeValue(fileStatusPath.toFile(), fileStatus); - mapper.writeValue(comletePiecesPath.toFile(), completePieces); + writeMaps(); + if (files.containsKey(fileId)) { try (FileOutputStream fout = new FileOutputStream(new File(filePaths.get(fileId)))) { fout.write(files.get(fileId)); @@ -84,10 +91,18 @@ public void save(int fileId) throws FileNotFoundException, IOException { } public void load() throws JsonGenerationException, JsonMappingException, IOException { - filePaths = mapper.readValue(filePathsPath.toFile(), filePaths.getClass()); - fileStatus = mapper.readValue(fileStatusPath.toFile(), fileStatus.getClass()); - completePieces = mapper.readValue(comletePiecesPath.toFile(), completePieces.getClass()); - + if (filePathsPath.toFile().exists()) + filePaths = mapper.readValue(filePathsPath.toFile(), filePaths.getClass()); + if (fileStatusPath.toFile().exists()) + fileStatus = mapper.readValue(fileStatusPath.toFile(), fileStatus.getClass()); + if (comletePiecesPath.toFile().exists()) + completePieces = mapper.readValue(comletePiecesPath.toFile(), completePieces.getClass()); + + if (!filePaths.keySet().equals(fileStatus.keySet()) + || !filePaths.keySet().equals(completePieces.keySet())) { + throw new RuntimeException("maps key sets not equal"); + } + for (Entry ent : filePaths.entrySet()) { try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { files.put(ent.getKey(), finp.readAllBytes()); @@ -112,7 +127,7 @@ public void addFileToDownload(int id, long size, String filePath) throws FilePro files.put(id, new byte[Math.toIntExact(size)]); filePaths.put(id, filePath); completePieces.put(id, ConcurrentHashMap.newKeySet()); - save(-1); + writeMaps(); } public void addExistingFile(int id, Path path) throws FileProblemException, FileNotFoundException, IOException { @@ -122,7 +137,7 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File if (filePaths.containsValue(path.toString())) { throw new FileProblemException("file with specified path used"); } - + try (FileInputStream finp = new FileInputStream(path.toFile())) { files.put(id, finp.readAllBytes()); } diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java index da5628d..9b8ec57 100644 --- a/torrent/src/main/java/torrent/client/Main.java +++ b/torrent/src/main/java/torrent/client/Main.java @@ -1,71 +1,22 @@ package torrent.client; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.Scanner; -import java.util.concurrent.ExecutionException; - -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; - -import torrent.common.ConcreteTaskHandler; -import torrent.common.RequestCompletionHandler; -import torrent.common.ServerRequestHandler; -import torrent.server.ListHandler; -import torrent.server.OldClientsCleaner; -import torrent.server.ServerData; -import torrent.server.SourcesHandler; -import torrent.server.UpdateHandler; -import torrent.server.UploadHandler; - public class Main { - - - public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException, InterruptedException, ExecutionException { + + public static void main(String[] args) { + + String host = args.length == 0 ? "localhost" : args[0]; + + Thread t = new Thread(new MainInner(host, System.out, System.in)); + t.start(); - final SocketAddress toServer = new InetSocketAddress(args[0], 8081); - - FilesHolder filesHolder = new FilesHolder(); - Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Saving client state..."); - try { - filesHolder.save(); - } catch (JsonGenerationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (JsonMappingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + t.interrupt(); } }); - - Thread srvThread = new Thread(new ServerProcess(new ConcreteTaskHandler[] { - new StatHandler(filesHolder), - new GetHandler(filesHolder) - })); - - srvThread.setDaemon(true); - srvThread.start(); - REPL repl = new REPL(filesHolder, new FilesDownloader(filesHolder, toServer), toServer); - repl.startRepl(); } } diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java new file mode 100644 index 0000000..b6723b1 --- /dev/null +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -0,0 +1,68 @@ +package torrent.client; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import torrent.common.ConcreteTaskHandler; +import torrent.common.ServerProcess; + +public class MainInner implements Runnable { + + private final String host; + private final PrintStream out; + private final InputStream sinp; + + public MainInner(String host, PrintStream out, InputStream sinp) { + this.host = host; + this.out = out; + this.sinp = sinp; + } + + @Override + public void run() { + FilesHolder filesHolder = null; + try { + final int myport = 8082; + final SocketAddress toServer = new InetSocketAddress(host, 8081); + + filesHolder = new FilesHolder(); + + ExecutorService exec = Executors.newFixedThreadPool(3); + exec.execute(new ServerProcess( + myport, + new ConcreteTaskHandler[] { + new StatHandler(filesHolder), + new GetHandler(filesHolder) + })); + + exec.execute(new UpdatePerformer(filesHolder, toServer, myport)); + + REPL repl = new REPL( + filesHolder, + new FilesDownloader(filesHolder, toServer), + toServer, + out, + sinp); + + exec.execute(() -> repl.startRepl()); + + while (true) { + Thread.sleep(10000); + } + } catch (IOException e) { + out.println(e.getMessage()); + } catch (InterruptedException e) { + out.println("Saving client state..."); + try { + filesHolder.save(); + } catch (IOException e1) { + out.print(e.getMessage()); + } + } + } +} diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index b8521da..fc466a6 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -4,7 +4,6 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; -import java.util.Set; public class PieceDownloader implements CompletionHandler { private int fileId; diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index 5b71dee..ffe9c6d 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -2,6 +2,7 @@ import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.PrintStream; import java.net.SocketAddress; import java.nio.file.Paths; @@ -18,12 +19,20 @@ public class REPL { private Map listedFilesName = new HashMap<>(); private SocketAddress toServer; - PrintStream out = System.out; - - public REPL(FilesHolder filesHolder, FilesDownloader downloader, SocketAddress toServer) { + private final PrintStream out; + private final InputStream inStream; + + public REPL( + FilesHolder filesHolder, + FilesDownloader downloader, + SocketAddress toServer, + PrintStream out, + InputStream inStream) { this.filesHolder = filesHolder; this.downloader = downloader; this.toServer = toServer; + this.out = out; + this.inStream = inStream; } void printStatus() { @@ -113,18 +122,18 @@ void listAvaliableFiles() { out.println(e.getMessage()); } } - + final String helpMessage = "This is torrent client.\n" - + "commands:\n" - + "list\n" - + "publish \n" - + "delete \n" - + "get \n" - + "pause \n" - + "status\n" - + "help"; - + + "commands:\n" + + "list\n" + + "publish \n" + + "delete \n" + + "get \n" + + "pause \n" + + "status\n" + + "help"; + public void startRepl() { /* Options options = new Options(); @@ -137,11 +146,12 @@ public void startRepl() { options.addOption("status", true, "list files known by your client and their status"); */ - try (Scanner in = new Scanner(System.in)) { + try (Scanner in = new Scanner(inStream)) { while (true) { try { - System.out.print(">"); - switch (in.next()) { + out.print(">"); + String command = in.next(); + switch (command) { case "help": { out.println(helpMessage); @@ -176,6 +186,11 @@ public void startRepl() { printStatus(); } break; + + default: + { + out.println("unknown command " + command); + } } } catch (Exception e) { out.println(e.getMessage()); diff --git a/torrent/src/main/java/torrent/client/ServerFilesLister.java b/torrent/src/main/java/torrent/client/ServerFilesLister.java index fd02771..d9fda41 100644 --- a/torrent/src/main/java/torrent/client/ServerFilesLister.java +++ b/torrent/src/main/java/torrent/client/ServerFilesLister.java @@ -9,11 +9,14 @@ public class ServerFilesLister { public static void list(SocketAddress addr, Map fileSizes, Map filesNames) throws IOException { - try ( - Socket s = new Socket(); - DataOutputStream dout = new DataOutputStream(s.getOutputStream()); - DataInputStream dinp = new DataInputStream(s.getInputStream())) - { + Socket s = null; + DataOutputStream dout = null; + DataInputStream dinp = null; + try { + s = new Socket(); + dout = new DataOutputStream(s.getOutputStream()); + dinp = new DataInputStream(s.getInputStream()); + s.connect(addr); dout.writeByte(1); @@ -30,6 +33,10 @@ public static void list(SocketAddress addr, Map fileSizes, Map Date: Mon, 17 Dec 2018 22:00:06 +0300 Subject: [PATCH 08/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 2 +- .../java/torrent/client/UpdatePerformer.java | 2 +- .../common/RequestCompletionHandler.java | 8 +++++++ .../java/torrent/common/ServerProcess.java | 4 ++++ .../torrent/common/ServerRequestHandler.java | 3 ++- .../main/java/torrent/server/ListHandler.java | 13 +++++++++--- .../src/main/java/torrent/server/Main.java | 2 +- .../main/java/torrent/server/MainInner.java | 21 ++++++++++++++++--- .../torrent/server/OldClientsCleaner.java | 17 +++++++++++---- .../java/torrent/server/SourcesHandler.java | 6 ++++-- .../java/torrent/server/UpdateHandler.java | 4 +++- .../java/torrent/server/UploadHandler.java | 2 +- 12 files changed, 66 insertions(+), 18 deletions(-) diff --git a/torrent/pom.xml b/torrent/pom.xml index 0313e2e..604faf6 100644 --- a/torrent/pom.xml +++ b/torrent/pom.xml @@ -78,7 +78,7 @@ junit junit - 3.8.1 + 4.12 test diff --git a/torrent/src/main/java/torrent/client/UpdatePerformer.java b/torrent/src/main/java/torrent/client/UpdatePerformer.java index 1cd5569..037fdcd 100644 --- a/torrent/src/main/java/torrent/client/UpdatePerformer.java +++ b/torrent/src/main/java/torrent/client/UpdatePerformer.java @@ -9,7 +9,7 @@ public class UpdatePerformer implements Runnable { private SocketAddress toServer; private FilesHolder filesHolder; - private int myPort; + private final int myPort; public UpdatePerformer(FilesHolder filesHolder, SocketAddress toServer, int myPort) { this.filesHolder = filesHolder; diff --git a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java index 5aa0476..c16ed08 100644 --- a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java +++ b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java @@ -52,6 +52,14 @@ public void failed(Throwable exc, Object attachment) { if (status == MessageProcessStatus.INCOMPLETE){ clientChannel.read(handler.getReceivingBuffer(), handler, this); } + + if (status == MessageProcessStatus.ERROR) { + try { + clientChannel.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } @Override diff --git a/torrent/src/main/java/torrent/common/ServerProcess.java b/torrent/src/main/java/torrent/common/ServerProcess.java index ce8d885..f69ff39 100644 --- a/torrent/src/main/java/torrent/common/ServerProcess.java +++ b/torrent/src/main/java/torrent/common/ServerProcess.java @@ -35,8 +35,12 @@ public void run() { ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); + try { clientChannel.read(handler.getReceivingBuffer(), handler, new RequestCompletionHandler(clientChannel)); + } catch (Exception e) { + System.out.println("Server: Exception while reading from channel."); + } } } diff --git a/torrent/src/main/java/torrent/common/ServerRequestHandler.java b/torrent/src/main/java/torrent/common/ServerRequestHandler.java index a19b8bc..e8ac4a0 100644 --- a/torrent/src/main/java/torrent/common/ServerRequestHandler.java +++ b/torrent/src/main/java/torrent/common/ServerRequestHandler.java @@ -43,7 +43,8 @@ public MessageProcessStatus messageProcessAttemp(InetSocketAddress clientInf) th return MessageProcessStatus.INCOMPLETE; } - if (typeIndex > handlers.length) { + if (typeIndex > handlers.length || typeIndex < 1) { + System.out.println("Operation index is bad"); return MessageProcessStatus.ERROR; } diff --git a/torrent/src/main/java/torrent/server/ListHandler.java b/torrent/src/main/java/torrent/server/ListHandler.java index 3528b1e..d0dd6c2 100644 --- a/torrent/src/main/java/torrent/server/ListHandler.java +++ b/torrent/src/main/java/torrent/server/ListHandler.java @@ -4,6 +4,9 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; @@ -16,13 +19,17 @@ public ListHandler(StorageManager stm) { @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf){ try { - out.writeInt(storage.data.map.size()); - - for(FileInformation d : storage.data.map.values()) { + + List cl = new ArrayList<>(storage.data.map.values()); + + out.writeInt(cl.size()); + + for(FileInformation d : cl) { out.writeInt(d.id); out.writeUTF(d.name); out.writeLong(d.size); } + } catch (IOException e) { return MessageProcessStatus.ERROR; } diff --git a/torrent/src/main/java/torrent/server/Main.java b/torrent/src/main/java/torrent/server/Main.java index 4f320df..51321d9 100644 --- a/torrent/src/main/java/torrent/server/Main.java +++ b/torrent/src/main/java/torrent/server/Main.java @@ -3,7 +3,7 @@ public class Main { public static void main(String[] args) { - Thread t = new Thread(new MainInner()); + Thread t = new Thread(new MainInner(5 * 60 * 1000)); t.start(); Runtime.getRuntime().addShutdownHook(new Thread() { diff --git a/torrent/src/main/java/torrent/server/MainInner.java b/torrent/src/main/java/torrent/server/MainInner.java index f5b95cc..da536ac 100644 --- a/torrent/src/main/java/torrent/server/MainInner.java +++ b/torrent/src/main/java/torrent/server/MainInner.java @@ -6,18 +6,26 @@ import torrent.common.ServerProcess; public class MainInner implements Runnable { - + + private final long updateMillis; + + public MainInner (long updateMillis) { + this.updateMillis = updateMillis; + } + @Override public void run() { StorageManager storageManager = null; + Thread cleanThread = null; + Thread srvThread = null; try { storageManager = new StorageManager("serverFile"); - Thread cleanThread = new Thread(new OldClientsCleaner(storageManager)); + cleanThread = new Thread(new OldClientsCleaner(storageManager, updateMillis)); cleanThread.setDaemon(true); cleanThread.start(); - Thread srvThread = new Thread(new ServerProcess( + srvThread = new Thread(new ServerProcess( 8081, new ConcreteTaskHandler[] { new ListHandler(storageManager), @@ -36,9 +44,16 @@ public void run() { } catch (InterruptedException e) { System.out.println("Saving server state..."); try { + cleanThread.interrupt(); + cleanThread.join(); + srvThread.interrupt(); + srvThread.join(); storageManager.save(); } catch (IOException e1) { e1.printStackTrace(); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); } } } diff --git a/torrent/src/main/java/torrent/server/OldClientsCleaner.java b/torrent/src/main/java/torrent/server/OldClientsCleaner.java index 68c933b..99f1c47 100644 --- a/torrent/src/main/java/torrent/server/OldClientsCleaner.java +++ b/torrent/src/main/java/torrent/server/OldClientsCleaner.java @@ -2,12 +2,13 @@ public class OldClientsCleaner implements Runnable { - public final long updateTime = 5 * 60 * 1000; + public final long updateTime;// = 5 * 60 * 1000; private StorageManager storage; - public OldClientsCleaner(StorageManager storage) { + public OldClientsCleaner(StorageManager storage, long updateTimeMillis) { this.storage = storage; + updateTime = updateTimeMillis; } @Override @@ -18,7 +19,9 @@ public void run() { } catch (InterruptedException e) { return; } - + + System.out.println("Cleaner loop"); + long currTime = System.currentTimeMillis(); storage.data.lastClientUpdate @@ -26,7 +29,13 @@ public void run() { .removeIf(ent -> currTime - ent.getValue() > updateTime); storage.data.map.values().stream().forEach(fInf -> - fInf.clients.removeIf(s -> !storage.data.lastClientUpdate.containsKey(s))); + fInf.clients.removeIf(s -> { + boolean res = !storage.data.lastClientUpdate.containsKey(s); + if (res) { + System.out.println("ServerCleaner: removing " + s); + } + return res; + })); try { storage.save(); diff --git a/torrent/src/main/java/torrent/server/SourcesHandler.java b/torrent/src/main/java/torrent/server/SourcesHandler.java index 05eb190..fbca15d 100644 --- a/torrent/src/main/java/torrent/server/SourcesHandler.java +++ b/torrent/src/main/java/torrent/server/SourcesHandler.java @@ -5,6 +5,7 @@ import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.HashSet; import java.util.Set; import torrent.common.ServerRequestHandler.MessageProcessStatus; @@ -21,10 +22,11 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o int id = in.readInt(); if (!storage.data.map.containsKey(id)) { - return MessageProcessStatus.ERROR; + out.writeInt(0); + return MessageProcessStatus.SUCCESS; } - Set clients = storage.data.map.get(id).clients; + Set clients = new HashSet<>(storage.data.map.get(id).clients); out.writeInt(clients.size()); diff --git a/torrent/src/main/java/torrent/server/UpdateHandler.java b/torrent/src/main/java/torrent/server/UpdateHandler.java index 69935a1..7d508b8 100644 --- a/torrent/src/main/java/torrent/server/UpdateHandler.java +++ b/torrent/src/main/java/torrent/server/UpdateHandler.java @@ -28,11 +28,13 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o FileInformation fInf = storage.data.map.get(id); if (fInf == null) { - return MessageProcessStatus.ERROR; + out.writeBoolean(false); + return MessageProcessStatus.SUCCESS; } fInf.clients.add(clientAddr); storage.data.lastClientUpdate.put(clientAddr, System.currentTimeMillis()); + System.out.println("Update handler: " + clientAddr + " added"); } storage.save(); out.writeBoolean(true); diff --git a/torrent/src/main/java/torrent/server/UploadHandler.java b/torrent/src/main/java/torrent/server/UploadHandler.java index e188e87..3c4ccb0 100644 --- a/torrent/src/main/java/torrent/server/UploadHandler.java +++ b/torrent/src/main/java/torrent/server/UploadHandler.java @@ -23,7 +23,7 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o long size = in.readLong(); int id = storage.data.filesCount++; - storage.data.map.put(id, new FileInformation(id, name, size, Arrays.asList(clientInf))); + storage.data.map.put(id, new FileInformation(id, name, size, Arrays.asList())); storage.save(); out.writeInt(id); } catch (EOFException e) { From f26b2916dcb3a39919a693c734e7d6caa37d1419 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 17 Dec 2018 22:37:19 +0300 Subject: [PATCH 09/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=81=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=D0=BA=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сделаны join потоков сервера. --- README.md | 12 +++++++++++- .../src/main/java/torrent/common/ServerProcess.java | 5 +++++ torrent/src/main/java/torrent/server/MainInner.java | 8 ++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4314260..e10c59e 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ -# java_homeworks \ No newline at end of file +### Torrent + +Для сборки выполнить + +``` +mvn install + +``` + +В папке target появятся client.jar и server.jar . +Клиенту при запуске можно передать имя хоста. По умолчанию localhost. diff --git a/torrent/src/main/java/torrent/common/ServerProcess.java b/torrent/src/main/java/torrent/common/ServerProcess.java index f69ff39..ab4372b 100644 --- a/torrent/src/main/java/torrent/common/ServerProcess.java +++ b/torrent/src/main/java/torrent/common/ServerProcess.java @@ -27,6 +27,11 @@ public void run() { try { clientChannel = srvChannel.accept().get(); } catch (InterruptedException e) { + try { + srvChannel.close(); + } catch (IOException e1) { + System.out.println("Exception while closing srvChannel " + e1.getMessage());; + } return; } catch (ExecutionException e) { // TODO Auto-generated catch block diff --git a/torrent/src/main/java/torrent/server/MainInner.java b/torrent/src/main/java/torrent/server/MainInner.java index da536ac..436bf88 100644 --- a/torrent/src/main/java/torrent/server/MainInner.java +++ b/torrent/src/main/java/torrent/server/MainInner.java @@ -6,13 +6,13 @@ import torrent.common.ServerProcess; public class MainInner implements Runnable { - + private final long updateMillis; - + public MainInner (long updateMillis) { this.updateMillis = updateMillis; } - + @Override public void run() { StorageManager storageManager = null; @@ -45,8 +45,8 @@ public void run() { System.out.println("Saving server state..."); try { cleanThread.interrupt(); - cleanThread.join(); srvThread.interrupt(); + cleanThread.join(); srvThread.join(); storageManager.save(); } catch (IOException e1) { From 782cd47a6a37fbd07ce8e2e8f27a8dbd015ca1de Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 19 Dec 2018 00:36:39 +0300 Subject: [PATCH 10/25] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D0=B0=20=D0=B2=20=D0=B1?= =?UTF-8?q?=D1=83=D1=84=D0=B5=D1=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/torrent/common/ServerRequestHandler.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/torrent/src/main/java/torrent/common/ServerRequestHandler.java b/torrent/src/main/java/torrent/common/ServerRequestHandler.java index e8ac4a0..07f9254 100644 --- a/torrent/src/main/java/torrent/common/ServerRequestHandler.java +++ b/torrent/src/main/java/torrent/common/ServerRequestHandler.java @@ -22,7 +22,6 @@ public ServerRequestHandler(ConcreteTaskHandler[] handlers) { } public ByteBuffer getReceivingBuffer() { - inputBuffer.clear(); return inputBuffer; } @@ -30,8 +29,8 @@ public MessageProcessStatus messageProcessAttemp(InetSocketAddress clientInf) th try ( DataInputStream dInp = new DataInputStream( new ByteArrayInputStream(inputBuffer.array(), - inputBuffer.arrayOffset(), - inputBuffer.limit() - inputBuffer.position())); + 0, + inputBuffer.position())); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dOut = new DataOutputStream(bout); From 2cb97f250d415cee26b8dc72b935ad18972d9056 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 19 Dec 2018 19:55:41 +0300 Subject: [PATCH 11/25] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B1=D0=B0=D0=B3=D0=B8=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/torrent/common/FileInformation.java | 9 +- .../common/RequestCompletionHandler.java | 2 +- .../java/torrent/common/ServerProcess.java | 18 +- .../main/java/torrent/server/ListHandler.java | 8 +- .../torrent/server/OldClientsCleaner.java | 8 +- .../main/java/torrent/server/ServerData.java | 2 - .../java/torrent/server/SourcesHandler.java | 8 +- .../java/torrent/server/StorageManager.java | 10 +- .../java/torrent/server/UpdateHandler.java | 11 +- .../java/torrent/server/UploadHandler.java | 2 +- .../test/java/torrent/server/ServerTests.java | 275 ++++++++++++++++++ 11 files changed, 322 insertions(+), 31 deletions(-) create mode 100644 torrent/src/test/java/torrent/server/ServerTests.java diff --git a/torrent/src/main/java/torrent/common/FileInformation.java b/torrent/src/main/java/torrent/common/FileInformation.java index 9786fc6..6cbd2da 100644 --- a/torrent/src/main/java/torrent/common/FileInformation.java +++ b/torrent/src/main/java/torrent/common/FileInformation.java @@ -9,13 +9,14 @@ public class FileInformation { public int id; public String name; public long size; - public Set clients; + //public Set clients; - public FileInformation(int id2, String name2, long size2, List clients2) { + public FileInformation() {}; + public FileInformation(int id2, String name2, long size2) { id = id2; name = name2; size = size2; - clients = new HashSet<>(); - clients.addAll(clients2); + //clients = new HashSet<>(); + //clients.addAll(clients2); } } diff --git a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java index c16ed08..7835989 100644 --- a/torrent/src/main/java/torrent/common/RequestCompletionHandler.java +++ b/torrent/src/main/java/torrent/common/RequestCompletionHandler.java @@ -52,7 +52,7 @@ public void failed(Throwable exc, Object attachment) { if (status == MessageProcessStatus.INCOMPLETE){ clientChannel.read(handler.getReceivingBuffer(), handler, this); } - + if (status == MessageProcessStatus.ERROR) { try { clientChannel.close(); diff --git a/torrent/src/main/java/torrent/common/ServerProcess.java b/torrent/src/main/java/torrent/common/ServerProcess.java index ab4372b..dc7e5ed 100644 --- a/torrent/src/main/java/torrent/common/ServerProcess.java +++ b/torrent/src/main/java/torrent/common/ServerProcess.java @@ -7,19 +7,19 @@ import java.util.concurrent.ExecutionException; public class ServerProcess implements Runnable { - + final int myPort; - + private final AsynchronousServerSocketChannel srvChannel = AsynchronousServerSocketChannel.open(); - + private final ConcreteTaskHandler[] concreteHandlers; - + public ServerProcess(int port, ConcreteTaskHandler[] concreteHandlers) throws IOException { myPort = port; this.concreteHandlers = concreteHandlers; srvChannel.bind(new InetSocketAddress(myPort)); } - + @Override public void run() { while (true) { @@ -37,12 +37,12 @@ public void run() { // TODO Auto-generated catch block e.printStackTrace(); } - + ServerRequestHandler handler = new ServerRequestHandler(concreteHandlers); - + try { - clientChannel.read(handler.getReceivingBuffer(), handler, - new RequestCompletionHandler(clientChannel)); + clientChannel.read(handler.getReceivingBuffer(), handler, + new RequestCompletionHandler(clientChannel)); } catch (Exception e) { System.out.println("Server: Exception while reading from channel."); } diff --git a/torrent/src/main/java/torrent/server/ListHandler.java b/torrent/src/main/java/torrent/server/ListHandler.java index d0dd6c2..f7deb1d 100644 --- a/torrent/src/main/java/torrent/server/ListHandler.java +++ b/torrent/src/main/java/torrent/server/ListHandler.java @@ -19,17 +19,17 @@ public ListHandler(StorageManager stm) { @Override public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream out, InetSocketAddress clientInf){ try { - + List cl = new ArrayList<>(storage.data.map.values()); - + out.writeInt(cl.size()); - + for(FileInformation d : cl) { out.writeInt(d.id); out.writeUTF(d.name); out.writeLong(d.size); } - + } catch (IOException e) { return MessageProcessStatus.ERROR; } diff --git a/torrent/src/main/java/torrent/server/OldClientsCleaner.java b/torrent/src/main/java/torrent/server/OldClientsCleaner.java index 99f1c47..d4a1023 100644 --- a/torrent/src/main/java/torrent/server/OldClientsCleaner.java +++ b/torrent/src/main/java/torrent/server/OldClientsCleaner.java @@ -24,13 +24,13 @@ public void run() { long currTime = System.currentTimeMillis(); - storage.data.lastClientUpdate + storage.lastClientUpdate .entrySet() .removeIf(ent -> currTime - ent.getValue() > updateTime); - storage.data.map.values().stream().forEach(fInf -> - fInf.clients.removeIf(s -> { - boolean res = !storage.data.lastClientUpdate.containsKey(s); + storage.clients.values().forEach(set -> + set.removeIf(s -> { + boolean res = !storage.lastClientUpdate.containsKey(s); if (res) { System.out.println("ServerCleaner: removing " + s); } diff --git a/torrent/src/main/java/torrent/server/ServerData.java b/torrent/src/main/java/torrent/server/ServerData.java index 5a932f4..2bae9cb 100644 --- a/torrent/src/main/java/torrent/server/ServerData.java +++ b/torrent/src/main/java/torrent/server/ServerData.java @@ -1,6 +1,5 @@ package torrent.server; -import java.net.InetSocketAddress; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -9,5 +8,4 @@ public class ServerData { public int filesCount = 0; public Map map = new ConcurrentHashMap<>(); - public Map lastClientUpdate = new ConcurrentHashMap<>(); } diff --git a/torrent/src/main/java/torrent/server/SourcesHandler.java b/torrent/src/main/java/torrent/server/SourcesHandler.java index fbca15d..c0b095f 100644 --- a/torrent/src/main/java/torrent/server/SourcesHandler.java +++ b/torrent/src/main/java/torrent/server/SourcesHandler.java @@ -7,6 +7,7 @@ import java.net.InetSocketAddress; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import torrent.common.ServerRequestHandler.MessageProcessStatus; @@ -25,12 +26,15 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o out.writeInt(0); return MessageProcessStatus.SUCCESS; } + if (storage.clients.get(id) == null) { + storage.clients.put(id, ConcurrentHashMap.newKeySet()); + } - Set clients = new HashSet<>(storage.data.map.get(id).clients); + Set clients = new HashSet<>(storage.clients.get(id)); out.writeInt(clients.size()); - for (InetSocketAddress addr : storage.data.map.get(id).clients) { + for (InetSocketAddress addr : storage.clients.get(id)) { out.write(addr.getAddress().getAddress()); out.writeShort(addr.getPort()); } diff --git a/torrent/src/main/java/torrent/server/StorageManager.java b/torrent/src/main/java/torrent/server/StorageManager.java index f05d349..4122465 100644 --- a/torrent/src/main/java/torrent/server/StorageManager.java +++ b/torrent/src/main/java/torrent/server/StorageManager.java @@ -1,8 +1,13 @@ package torrent.server; import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + import com.fasterxml.jackson.databind.ObjectMapper; import torrent.server.ServerData; @@ -15,12 +20,15 @@ public class StorageManager { //public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public volatile ServerData data = new ServerData(); - + public volatile Map lastClientUpdate = new ConcurrentHashMap<>(); + public volatile Map> clients = new ConcurrentHashMap<>(); + public StorageManager(String savePath) { this.savePath = Paths.get(savePath); try { load(); } catch (Exception e) { + e.printStackTrace(); data = new ServerData(); } } diff --git a/torrent/src/main/java/torrent/server/UpdateHandler.java b/torrent/src/main/java/torrent/server/UpdateHandler.java index 7d508b8..c625f29 100644 --- a/torrent/src/main/java/torrent/server/UpdateHandler.java +++ b/torrent/src/main/java/torrent/server/UpdateHandler.java @@ -5,6 +5,7 @@ import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.concurrent.ConcurrentHashMap; import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; @@ -31,9 +32,13 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o out.writeBoolean(false); return MessageProcessStatus.SUCCESS; } - - fInf.clients.add(clientAddr); - storage.data.lastClientUpdate.put(clientAddr, System.currentTimeMillis()); + + if (!storage.clients.containsKey(id)) { + new ConcurrentHashMap<>(); + storage.clients.put(id, ConcurrentHashMap.newKeySet()); + } + storage.clients.get(id).add(clientAddr); + storage.lastClientUpdate.put(clientAddr, System.currentTimeMillis()); System.out.println("Update handler: " + clientAddr + " added"); } storage.save(); diff --git a/torrent/src/main/java/torrent/server/UploadHandler.java b/torrent/src/main/java/torrent/server/UploadHandler.java index 3c4ccb0..a2bad36 100644 --- a/torrent/src/main/java/torrent/server/UploadHandler.java +++ b/torrent/src/main/java/torrent/server/UploadHandler.java @@ -23,7 +23,7 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o long size = in.readLong(); int id = storage.data.filesCount++; - storage.data.map.put(id, new FileInformation(id, name, size, Arrays.asList())); + storage.data.map.put(id, new FileInformation(id, name, size)); storage.save(); out.writeInt(id); } catch (EOFException e) { diff --git a/torrent/src/test/java/torrent/server/ServerTests.java b/torrent/src/test/java/torrent/server/ServerTests.java new file mode 100644 index 0000000..15e3b1b --- /dev/null +++ b/torrent/src/test/java/torrent/server/ServerTests.java @@ -0,0 +1,275 @@ +package torrent.server; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import torrent.server.MainInner; + + +public class ServerTests { + + Thread server; + + Socket socket = new Socket(); + + DataOutputStream out; + DataInputStream in; + + void establishConnection() throws IOException { + socket = new Socket("localhost", 8081); + out = new DataOutputStream(socket.getOutputStream()); + in = new DataInputStream(socket.getInputStream()); + } + + void closeConnection() throws IOException { + in.close(); + out.close(); + socket.close(); + } + + public void startServer() throws InterruptedException { + //Thread.sleep(1000); + server = new Thread(new MainInner(3000)); + server.start(); + Thread.sleep(1000); + } + + @Before + public void setUp() throws InterruptedException { + startServer(); + } + + + public void stopServer() throws IOException, InterruptedException { + System.out.println("Stopping server"); + server.interrupt(); + server.join(); + } + + @After + public void cleanUp() throws IOException, InterruptedException { + stopServer(); + Files.delete(Paths.get("serverFile")); + } + + @Test + public void testFantasy() throws UnknownHostException, IOException, InterruptedException { + + establishConnection(); + out.writeByte(1); //list + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(2); //upload + out.writeUTF("file1.txt"); + + out.writeLong(11); + + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(1); + assertEquals(1, in.readInt()); + assertEquals(0, in.readInt()); + assertEquals("file1.txt", in.readUTF()); + assertEquals(11, in.readLong()); + + closeConnection(); + establishConnection(); + System.out.println("Sources for non existing id"); + out.writeByte(3); //sources + out.writeInt(1); + + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(3); + out.writeInt(0); + + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(4); //update + out.writeShort(1234); + out.writeInt(1); + out.writeInt(0); + assertTrue(in.readBoolean()); + + closeConnection(); + establishConnection(); + + out.writeByte(3); + out.writeInt(0); + + assertEquals(1, in.readInt()); + + byte[] ip = new byte[4]; + in.readFully(ip); + assertArrayEquals(new byte[] {127,0,0,1}, ip); + + assertEquals(1234, in.readShort()); + + Thread.sleep(1000); + + closeConnection(); + establishConnection(); + + out.writeByte(2); //upload + out.writeUTF("file2.txt"); + + out.writeLong(12); + + assertEquals(1, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(1); + assertEquals(2, in.readInt()); + assertEquals(0, in.readInt()); + assertEquals("file1.txt", in.readUTF()); + assertEquals(11, in.readLong()); + assertEquals(1, in.readInt()); + assertEquals("file2.txt", in.readUTF()); + assertEquals(12, in.readLong()); + + Thread.sleep(2000); + + closeConnection(); + establishConnection(); + + out.writeByte(1); + assertEquals(2, in.readInt()); + assertEquals(0, in.readInt()); + assertEquals("file1.txt", in.readUTF()); + assertEquals(11, in.readLong()); + assertEquals(1, in.readInt()); + assertEquals("file2.txt", in.readUTF()); + assertEquals(12, in.readLong()); + + closeConnection(); + establishConnection(); + + out.writeByte(4); //update + out.writeShort(1235); + out.writeInt(1); + out.writeInt(1); + assertTrue(in.readBoolean()); + + closeConnection(); + establishConnection(); + //Thread.sleep(1000); + out.writeByte(3); //sources + out.writeInt(1); + System.out.println("before 1 sources"); + assertEquals(1, in.readInt()); + + do { + closeConnection(); + establishConnection(); + + out.writeByte(3); //sources + out.writeInt(0); + + Thread.sleep(10); + } while (0 != in.readInt()); + } + + @Test + public void testSourcesCleaner() throws IOException, InterruptedException { + establishConnection(); + out.writeByte(1); //list + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(2); //upload + out.writeUTF("file1.txt"); + + out.writeLong(11); + + assertEquals(0, in.readInt()); + + closeConnection(); + establishConnection(); + + out.writeByte(4); //update + out.writeShort(1235); + out.writeInt(1); + out.writeInt(0); + assertTrue(in.readBoolean()); + + do { + closeConnection(); + establishConnection(); + + out.writeByte(3); //sources + out.writeInt(0); + + Thread.sleep(10); + //assertEquals(0, in.readInt()); + } while (0 != in.readInt()); + + } + + @Test + public void testSavingState() throws IOException, InterruptedException { + establishConnection(); + + out.writeByte(2); //upload + out.writeUTF("file1.txt"); + + out.writeLong(11); + closeConnection(); + establishConnection(); + + out.writeByte(2); //upload + out.writeUTF("file2.txt"); + + out.writeLong(12); + + assertEquals(1, in.readInt()); + + closeConnection(); + stopServer(); + Thread.sleep(2000); + startServer(); + + establishConnection(); + + out.writeByte(1); + + System.out.println("check count"); + assertEquals(2, in.readInt()); + + assertEquals(0, in.readInt()); + assertEquals("file1.txt", in.readUTF()); + assertEquals(11, in.readLong()); + assertEquals(1, in.readInt()); + assertEquals("file2.txt", in.readUTF()); + assertEquals(12, in.readLong()); + } + +} From b1279223059324c3e81929fae4b8cfb56d67f72b Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 20 Dec 2018 00:30:36 +0300 Subject: [PATCH 12/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/pom.xml | 6 + .../java/torrent/client/FilesDownloader.java | 4 + .../main/java/torrent/client/FilesHolder.java | 31 ++-- .../src/main/java/torrent/client/Main.java | 6 +- .../main/java/torrent/client/MainInner.java | 16 +- .../src/main/java/torrent/client/REPL.java | 28 +++- .../torrent/client/ServerFilesLister.java | 22 ++- .../java/torrent/client/UpdatePerformer.java | 8 +- .../main/java/torrent/client/Uploader.java | 18 +- .../java/torrent/common/FileInformation.java | 8 - .../java/torrent/server/UploadHandler.java | 2 - .../test/java/torrent/InteractionTests.java | 156 ++++++++++++++++++ 12 files changed, 247 insertions(+), 58 deletions(-) create mode 100644 torrent/src/test/java/torrent/InteractionTests.java diff --git a/torrent/pom.xml b/torrent/pom.xml index 604faf6..c7926bf 100644 --- a/torrent/pom.xml +++ b/torrent/pom.xml @@ -89,5 +89,11 @@ 2.9.7 + + + org.apache.commons + commons-io + 1.3.2 + diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index 835fa33..914db4c 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -36,6 +36,10 @@ public boolean startFileDownload(int fileId) { } public void stopFileDownload(int fileId) { + if (!filesHolder.fileStatus.containsKey(fileId)) { + throw new NullPointerException("This file wasn't been downloading"); + } + if (filesHolder.fileStatus.get(fileId) != FileStatus.Downloading) { return; } diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 111b1e7..9e9cc35 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -31,10 +31,10 @@ public enum FileStatus {Complete, Downloading, Paused}; // ObjectMapper mapper = new ObjectMapper(); - private final Path mapPath = Paths.get("torrentData"); - private final Path filePathsPath = mapPath.resolve("filepaths"); - private final Path fileStatusPath = mapPath.resolve("filesStatus"); - private final Path comletePiecesPath = mapPath.resolve("completePieces"); + private final Path mapPath; + private final Path filePathsPath; + private final Path fileStatusPath; + private final Path comletePiecesPath; public int numParts(int fileId) { return (files.get(fileId).length + pieceSize - 1) / pieceSize; @@ -51,13 +51,17 @@ public int pieceLenght(int fileId, int numPart) { : (numPart + 1) * pieceSize - file_length; } - public FilesHolder() throws IOException { + public FilesHolder(String path) throws IOException { + mapPath = Paths.get(path); + filePathsPath = mapPath.resolve("filepaths"); + fileStatusPath = mapPath.resolve("filesStatus"); + comletePiecesPath = mapPath.resolve("completePieces"); load(); } - + private void writeMaps() throws IOException { Files.createDirectory(mapPath); - + filePathsPath.toFile().createNewFile(); mapper.writeValue(filePathsPath.toFile(), filePaths); @@ -67,7 +71,7 @@ private void writeMaps() throws IOException { comletePiecesPath.toFile().createNewFile(); mapper.writeValue(comletePiecesPath.toFile(), completePieces); } - + public void save() throws JsonGenerationException, JsonMappingException, IOException { writeMaps(); @@ -80,7 +84,7 @@ public void save() throws JsonGenerationException, JsonMappingException, IOExcep } public void save(int fileId) throws FileNotFoundException, IOException { - + writeMaps(); if (files.containsKey(fileId)) { @@ -97,12 +101,12 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep fileStatus = mapper.readValue(fileStatusPath.toFile(), fileStatus.getClass()); if (comletePiecesPath.toFile().exists()) completePieces = mapper.readValue(comletePiecesPath.toFile(), completePieces.getClass()); - + if (!filePaths.keySet().equals(fileStatus.keySet()) || !filePaths.keySet().equals(completePieces.keySet())) { throw new RuntimeException("maps key sets not equal"); } - + for (Entry ent : filePaths.entrySet()) { try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { files.put(ent.getKey(), finp.readAllBytes()); @@ -110,7 +114,10 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep } } - public void deleteFile(int id) { + public void deleteFile(int id) { + if (! files.containsKey(id)) { + throw new NullPointerException("File isn't known."); + } files.remove(id); filePaths.remove(id); fileStatus.remove(id); diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java index 9b8ec57..259fb84 100644 --- a/torrent/src/main/java/torrent/client/Main.java +++ b/torrent/src/main/java/torrent/client/Main.java @@ -1,12 +1,14 @@ package torrent.client; +import java.io.IOException; + public class Main { - public static void main(String[] args) { + public static void main(String[] args) throws IOException { String host = args.length == 0 ? "localhost" : args[0]; - Thread t = new Thread(new MainInner(host, System.out, System.in)); + Thread t = new Thread(new MainInner(host, 8082, System.out, System.in, 100000, new FilesHolder("torrentData"))); t.start(); Runtime.getRuntime().addShutdownHook(new Thread() { diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java index b6723b1..ea8c0c6 100644 --- a/torrent/src/main/java/torrent/client/MainInner.java +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -16,22 +16,24 @@ public class MainInner implements Runnable { private final String host; private final PrintStream out; private final InputStream sinp; - - public MainInner(String host, PrintStream out, InputStream sinp) { + private final long updateTime; + private final FilesHolder filesHolder; + private final int myport; + + public MainInner(String host, int port, PrintStream out, InputStream sinp, long updateTime, FilesHolder fh) { this.host = host; this.out = out; this.sinp = sinp; + this.updateTime = updateTime; + this.filesHolder = fh; + this.myport = port; } @Override public void run() { - FilesHolder filesHolder = null; try { - final int myport = 8082; final SocketAddress toServer = new InetSocketAddress(host, 8081); - filesHolder = new FilesHolder(); - ExecutorService exec = Executors.newFixedThreadPool(3); exec.execute(new ServerProcess( myport, @@ -40,7 +42,7 @@ public void run() { new GetHandler(filesHolder) })); - exec.execute(new UpdatePerformer(filesHolder, toServer, myport)); + exec.execute(new UpdatePerformer(filesHolder, toServer, myport, updateTime)); REPL repl = new REPL( filesHolder, diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index ffe9c6d..e3bf11b 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -37,6 +37,10 @@ public REPL( void printStatus() { + if (filesHolder.files.isEmpty()) { + out.println("No files yet"); + } + for (Integer id : filesHolder.files.keySet()) { out.print(id + " " + filesHolder.filePaths.get(id) + " "); switch (filesHolder.fileStatus.get(id)) { @@ -64,7 +68,7 @@ void printStatus() { void startDownload(int id, String filename) throws FileNotFoundException, FileProblemException, IOException { Long size = listedFilesSize.get(id); if (size == null) { - out.println("Unknown file size. Make list first."); + out.println("Unknown file id. Make list first."); return; } filesHolder.addFileToDownload(id, size, filename); @@ -72,11 +76,14 @@ void startDownload(int id, String filename) throws FileNotFoundException, FilePr } void deleteFile(int id) { - downloader.stopFileDownload(id); + try { + downloader.stopFileDownload(id); + } catch (NullPointerException e) {} + filesHolder.deleteFile(id); } - void publishFile(String pathTo) { + void publishFile(String pathTo) throws FileProblemException { try { int id = Uploader.uploadAndGetId(toServer, pathTo); if (id == 1) { @@ -85,10 +92,11 @@ void publishFile(String pathTo) { } filesHolder.addExistingFile(id, Paths.get(pathTo)); + } catch (FileNotFoundException e) { + out.println("Failed to publish file.\n" + + pathTo + " not found."); } catch (IOException e) { - out.println(e.getMessage()); - } catch (FileProblemException e) { - out.println(e.getMessage()); + out.println("Failed to publish file.\n" + e.getMessage()); } } @@ -96,6 +104,9 @@ void listAvaliableFiles() { try { ServerFilesLister.list(toServer, listedFilesSize, listedFilesName); + if (listedFilesName.isEmpty()) { + out.println("No files avaliable on server"); + } for (int id : listedFilesName.keySet()) { out.print(id + " " + listedFilesName.get(id)); switch (filesHolder.fileStatus.get(id)) { @@ -118,8 +129,7 @@ void listAvaliableFiles() { } } catch (IOException e) { - out.println("Failed to list avaliable files"); - out.println(e.getMessage()); + out.println("Failed to list avaliable files.\n" + e.getMessage()); } } @@ -192,7 +202,7 @@ public void startRepl() { out.println("unknown command " + command); } } - } catch (Exception e) { + } catch (IOException | FileProblemException | NullPointerException e) { out.println(e.getMessage()); } } diff --git a/torrent/src/main/java/torrent/client/ServerFilesLister.java b/torrent/src/main/java/torrent/client/ServerFilesLister.java index d9fda41..f4380d7 100644 --- a/torrent/src/main/java/torrent/client/ServerFilesLister.java +++ b/torrent/src/main/java/torrent/client/ServerFilesLister.java @@ -14,29 +14,33 @@ public static void list(SocketAddress addr, Map fileSizes, Map clients; public FileInformation() {}; public FileInformation(int id2, String name2, long size2) { id = id2; name = name2; size = size2; - //clients = new HashSet<>(); - //clients.addAll(clients2); } } diff --git a/torrent/src/main/java/torrent/server/UploadHandler.java b/torrent/src/main/java/torrent/server/UploadHandler.java index a2bad36..2dcb9b6 100644 --- a/torrent/src/main/java/torrent/server/UploadHandler.java +++ b/torrent/src/main/java/torrent/server/UploadHandler.java @@ -5,8 +5,6 @@ import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; -import java.util.Arrays; - import torrent.common.FileInformation; import torrent.common.ServerRequestHandler.MessageProcessStatus; diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java new file mode 100644 index 0000000..f652fec --- /dev/null +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -0,0 +1,156 @@ +package torrent; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Random; + +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import torrent.client.FilesHolder; +import torrent.server.MainInner; + +public class InteractionTests { + + Thread server; + + final int ncl = 3; + Thread[] cl = new Thread[ncl]; + + PrintWriter[] inp = new PrintWriter[ncl]; + ByteArrayOutputStream[] outp = new ByteArrayOutputStream[ncl]; + FilesHolder[] fh = new FilesHolder[ncl]; + + String getOutput(int i) throws InterruptedException { + Thread.sleep(1000); + String res = outp[i].toString(); + outp[i].reset(); + return res; + } + + void commandTo(int i, String s) { + inp[i].println(s); + inp[i].flush(); + } + + public void startServer() throws InterruptedException { + server = new Thread(new MainInner(3000)); + server.start(); + Thread.sleep(1000); + } + + public void initClient(int i) throws IOException { + outp[i] = new ByteArrayOutputStream(); + PipedInputStream pin = new PipedInputStream(); + inp[i] = new PrintWriter(new PipedOutputStream(pin)); + fh[i] = new FilesHolder("torrentData/client" + i); + cl[i] = new Thread(new torrent.client.MainInner("localhost", 8090 + i, + new PrintStream(outp[i]), + pin, + 2000, + fh[i])); + } + + public void createClientFiles() throws IOException { + byte[] arr = new byte[15 * (1 << 20)]; + Random r = new Random(5); + r.nextBytes(arr); + for (int i = 0; i < 2; i++) { + Files.createDirectories(Paths.get("torrentData/client" + i)); + OutputStream out = new FileOutputStream(new File("torrentData/client" + i + "/file1")); + out.write(arr); + out.close(); + } + } + + @Before + public void setUp() throws InterruptedException, IOException { + //startServer(); + for (int i = 0; i < 3; i++) { + initClient(i); + } + createClientFiles(); + } + + + public void stopServer() throws IOException, InterruptedException { + if (server == null) + return; + System.out.println("Stopping server"); + server.interrupt(); + server.join(); + } + + public void stopClients() throws InterruptedException { + for (int i = 0; i < 3; i++) { + cl[i].interrupt(); + cl[i].join(); + } + } + + @After + public void cleanUp() throws IOException, InterruptedException { + stopServer(); + stopClients(); + Files.deleteIfExists(Paths.get("serverFile")); + FileUtils.deleteDirectory(Paths.get("torrentData").toFile()); + } + + @Test + public void testBaseCommands() throws InterruptedException { + cl[0].start(); + + assertEquals(">", getOutput(0)); + + commandTo(0, "list"); + assertEquals( + "Failed to list avaliable files.\n" + + "Failed to connect to server\n" + + ">", getOutput(0)); + + commandTo(0, "status"); + assertEquals("No files yet\n>", getOutput(0)); + + commandTo(0, "publish nonExstFile"); + assertEquals("Failed to publish file.\n" + + "Failed to connect to server\n>", getOutput(0)); + + commandTo(0, "pause 1"); + assertEquals("This file wasn't been downloading\n>", getOutput(0)); + + commandTo(0, "delete 1"); + assertEquals("File isn't known.\n>", getOutput(0)); + + commandTo(0, "get 1 torrentData/client0/file1"); + assertEquals("Unknown file id. Make list first.\n>", getOutput(0)); + + //################################ + startServer(); + //################################ + + commandTo(0, "list"); + assertEquals("No files avaliable on server\n>", getOutput(0)); + + commandTo(0, "status"); + assertEquals("No files yet\n>", getOutput(0)); + + commandTo(0, "publish nonExstFile"); + assertEquals("Failed to publish file.\n" + + " nonExstFile not found.\n" + + ">", getOutput(0)); + } + +} From 620fedc90224ef817ca694cdd6e510f3498bcfb2 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 21 Dec 2018 01:08:39 +0300 Subject: [PATCH 13/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D0=BD=D0=B0=20=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81?= =?UTF-8?q?=20=D0=B4=D1=80=D1=83=D0=B3=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/torrent/client/FilesHolder.java | 27 +++++-- .../main/java/torrent/client/MainInner.java | 31 ++++++-- .../java/torrent/client/PieceDownloader.java | 4 +- .../src/main/java/torrent/client/REPL.java | 22 ++++-- .../torrent/client/SingleFileDownloader.java | 51 +++++++++++-- .../main/java/torrent/client/StatHandler.java | 2 +- .../main/java/torrent/client/Uploader.java | 6 +- .../torrent/server/OldClientsCleaner.java | 12 +-- .../java/torrent/server/UpdateHandler.java | 2 +- .../test/java/torrent/InteractionTests.java | 74 +++++++++++++++++-- 10 files changed, 183 insertions(+), 48 deletions(-) diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 9e9cc35..6c86dc9 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -12,7 +12,11 @@ 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; @@ -46,9 +50,9 @@ public int pieceOffset(int fileId, int numPart) { public int pieceLenght(int fileId, int numPart) { int file_length = files.get(fileId).length; - return (numPart + 1) * pieceSize >= file_length + return (numPart + 1) * pieceSize <= file_length ? pieceSize - : (numPart + 1) * pieceSize - file_length; + : file_length - numPart * pieceSize; } public FilesHolder(String path) throws IOException { @@ -60,7 +64,8 @@ public FilesHolder(String path) throws IOException { } private void writeMaps() throws IOException { - Files.createDirectory(mapPath); + if (!Files.exists(mapPath)) + Files.createDirectories(mapPath); filePathsPath.toFile().createNewFile(); mapper.writeValue(filePathsPath.toFile(), filePaths); @@ -96,12 +101,12 @@ public void save(int fileId) throws FileNotFoundException, IOException { public void load() throws JsonGenerationException, JsonMappingException, IOException { if (filePathsPath.toFile().exists()) - filePaths = mapper.readValue(filePathsPath.toFile(), filePaths.getClass()); + filePaths = mapper.readValue(filePathsPath.toFile(), new TypeReference>() {}); if (fileStatusPath.toFile().exists()) - fileStatus = mapper.readValue(fileStatusPath.toFile(), fileStatus.getClass()); + fileStatus = mapper.readValue(fileStatusPath.toFile(), new TypeReference>() {}); if (comletePiecesPath.toFile().exists()) - completePieces = mapper.readValue(comletePiecesPath.toFile(), completePieces.getClass()); - + completePieces = mapper.readValue(comletePiecesPath.toFile(), new TypeReference>>() {}); + if (!filePaths.keySet().equals(fileStatus.keySet()) || !filePaths.keySet().equals(completePieces.keySet())) { throw new RuntimeException("maps key sets not equal"); @@ -110,6 +115,9 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep for (Entry ent : filePaths.entrySet()) { try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { files.put(ent.getKey(), finp.readAllBytes()); + } catch (FileNotFoundException fnfe) { + completePieces.get(ent.getKey()).clear(); + fileStatus.put(ent.getKey(), FileStatus.Downloading); } } } @@ -134,6 +142,7 @@ public void addFileToDownload(int id, long size, String filePath) throws FilePro files.put(id, new byte[Math.toIntExact(size)]); filePaths.put(id, filePath); completePieces.put(id, ConcurrentHashMap.newKeySet()); + fileStatus.put(id, FileStatus.Paused); writeMaps(); } @@ -150,6 +159,10 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File } filePaths.put(id, path.toString()); fileStatus.put(id, FileStatus.Complete); + completePieces.put(id, + Stream.iterate(0, i -> i + 1) + .limit(numParts(id)) + .collect(Collectors.toSet())); save(-1); } diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java index ea8c0c6..31bb52b 100644 --- a/torrent/src/main/java/torrent/client/MainInner.java +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -5,9 +5,6 @@ import java.io.PrintStream; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - import torrent.common.ConcreteTaskHandler; import torrent.common.ServerProcess; @@ -19,7 +16,7 @@ public class MainInner implements Runnable { private final long updateTime; private final FilesHolder filesHolder; private final int myport; - + public MainInner(String host, int port, PrintStream out, InputStream sinp, long updateTime, FilesHolder fh) { this.host = host; this.out = out; @@ -31,18 +28,18 @@ public MainInner(String host, int port, PrintStream out, InputStream sinp, long @Override public void run() { + Thread server = null, updater = null, replTh = null; try { final SocketAddress toServer = new InetSocketAddress(host, 8081); - ExecutorService exec = Executors.newFixedThreadPool(3); - exec.execute(new ServerProcess( + server = new Thread(new ServerProcess( myport, new ConcreteTaskHandler[] { new StatHandler(filesHolder), new GetHandler(filesHolder) })); - exec.execute(new UpdatePerformer(filesHolder, toServer, myport, updateTime)); + updater = new Thread(new UpdatePerformer(filesHolder, toServer, myport, updateTime)); REPL repl = new REPL( filesHolder, @@ -51,7 +48,11 @@ public void run() { out, sinp); - exec.execute(() -> repl.startRepl()); + replTh = new Thread(() -> repl.startRepl()); + + server.start(); + updater.start(); + replTh.start(); while (true) { Thread.sleep(10000); @@ -59,10 +60,24 @@ public void run() { } catch (IOException e) { out.println(e.getMessage()); } catch (InterruptedException e) { + server.interrupt(); + updater.interrupt(); + replTh.interrupt(); + + try { + server.join(); + updater.join(); + replTh.join(); + } catch (InterruptedException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); + } + out.println("Saving client state..."); try { filesHolder.save(); } catch (IOException e1) { + out.println("Saving client state failed"); out.print(e.getMessage()); } } diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index fc466a6..b6de35b 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -36,6 +36,7 @@ public void completed(Integer result, AsynchronousSocketChannel attachment) { attachment.read(buffer, attachment, this); } else { filesDownloader.filesHolder.completePieces.get(fileId).add(pieceIdx); + filesDownloader.checkIfComplete(); try { attachment.close(); } catch (IOException e) { @@ -47,8 +48,7 @@ public void completed(Integer result, AsynchronousSocketChannel attachment) { @Override public void failed(Throwable exc, AsynchronousSocketChannel attachment) { - // TODO Auto-generated method stub - + System.err.println("PieceDownloader failed"); } diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index e3bf11b..4e2e0f0 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -8,6 +8,7 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Scanner; public class REPL { @@ -60,12 +61,13 @@ void printStatus() { break; } } - out.println(filesHolder.completePieces.get(id).size() + " / " + out.println(" " + filesHolder.completePieces.get(id).size() + "/" + filesHolder.numParts(id)); } } void startDownload(int id, String filename) throws FileNotFoundException, FileProblemException, IOException { + filename = filename.trim(); Long size = listedFilesSize.get(id); if (size == null) { out.println("Unknown file id. Make list first."); @@ -84,18 +86,18 @@ void deleteFile(int id) { } void publishFile(String pathTo) throws FileProblemException { + pathTo = pathTo.trim(); try { int id = Uploader.uploadAndGetId(toServer, pathTo); - if (id == 1) { - out.println("file not exist"); - return; - } filesHolder.addExistingFile(id, Paths.get(pathTo)); + out.println("The file has an id " + id); } catch (FileNotFoundException e) { + //e.printStackTrace(); out.println("Failed to publish file.\n" + - pathTo + " not found."); + pathTo + " not exists."); } catch (IOException e) { + //e.printStackTrace(); out.println("Failed to publish file.\n" + e.getMessage()); } } @@ -108,7 +110,11 @@ void listAvaliableFiles() { out.println("No files avaliable on server"); } for (int id : listedFilesName.keySet()) { - out.print(id + " " + listedFilesName.get(id)); + out.print(id + " " + listedFilesName.get(id) + " "); + if (!filesHolder.fileStatus.containsKey(id)) { + out.println(); + return; + } switch (filesHolder.fileStatus.get(id)) { case Complete: { @@ -204,6 +210,8 @@ public void startRepl() { } } catch (IOException | FileProblemException | NullPointerException e) { out.println(e.getMessage()); + } catch (NoSuchElementException e) { + return; } } } diff --git a/torrent/src/main/java/torrent/client/SingleFileDownloader.java b/torrent/src/main/java/torrent/client/SingleFileDownloader.java index 63a0097..bf80fd4 100644 --- a/torrent/src/main/java/torrent/client/SingleFileDownloader.java +++ b/torrent/src/main/java/torrent/client/SingleFileDownloader.java @@ -1,5 +1,6 @@ package torrent.client; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -7,12 +8,14 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.ExecutionException; import torrent.client.FilesHolder.FileStatus; @@ -92,7 +95,30 @@ private void updatePieceSources() { } } - private void dispatchPieceDownloaders() { + + private boolean getRequest(AsynchronousSocketChannel chan, int fileId, int partNum) throws IOException, InterruptedException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dout = new DataOutputStream(baos); + + dout.writeByte(2); + dout.writeInt(fileId); + dout.writeInt(partNum); + + dout.close(); + ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); + + do { + try { + chan.write(bb).get(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } while (bb.hasRemaining()); + + return true; + } + + private void dispatchPieceDownloaders() throws InterruptedException { for (int i = 0; i < numPieces; i++) { if (filesHolder.completePieces.get(fileId).contains(i)) { pieceChannels.remove(i); @@ -116,7 +142,22 @@ private void dispatchPieceDownloaders() { // TODO Auto-generated catch block e.printStackTrace(); } - chan.connect(sources.get(rIdx)); + try { + chan.connect(sources.get(rIdx)).get(); + } catch (ExecutionException e) { + continue; + } catch (InterruptedException e) { + return; + } + try { + if (!getRequest(chan, fileId, i)) { + System.out.println("SFD: get request failed"); + continue; + } + } catch (IOException e) { + e.printStackTrace(); + continue; + } PieceDownloader pdl = new PieceDownloader(this, fileId, i); chan.read(pdl.getBuffer(), chan, pdl); pieceChannels.put(i, chan); @@ -124,7 +165,7 @@ private void dispatchPieceDownloaders() { } } - private boolean checkIfComplete() { + boolean checkIfComplete() { if (filesHolder.completePieces.get(fileId).size() < filesHolder.numParts(fileId)) { return false; } @@ -157,9 +198,9 @@ public void run() { updateFileSources(); updatePieceSources(); - dispatchPieceDownloaders(); - + try { + dispatchPieceDownloaders(); Thread.sleep(10000); } catch (InterruptedException e) { return; diff --git a/torrent/src/main/java/torrent/client/StatHandler.java b/torrent/src/main/java/torrent/client/StatHandler.java index baf4305..aa429d1 100644 --- a/torrent/src/main/java/torrent/client/StatHandler.java +++ b/torrent/src/main/java/torrent/client/StatHandler.java @@ -32,7 +32,7 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o out.writeInt(pieces.size()); for (int i : pieces) { - out.write(i); + out.writeInt(i); } return MessageProcessStatus.SUCCESS; diff --git a/torrent/src/main/java/torrent/client/Uploader.java b/torrent/src/main/java/torrent/client/Uploader.java index 719c8c8..656d049 100644 --- a/torrent/src/main/java/torrent/client/Uploader.java +++ b/torrent/src/main/java/torrent/client/Uploader.java @@ -2,6 +2,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; @@ -21,9 +22,10 @@ public static int uploadAndGetId(SocketAddress addr, String filePath) throws IOE dout = new DataOutputStream(s.getOutputStream()); dinp = new DataInputStream(s.getInputStream()); - if (!Files.isRegularFile(Paths.get(filePath))) { - return -1; + if (!Files.exists(Paths.get(filePath))) { + throw new FileNotFoundException(); } + long len = Paths.get(filePath).toFile().length(); dout.writeByte(2); dout.writeUTF(Paths.get(filePath).getFileName().toString()); diff --git a/torrent/src/main/java/torrent/server/OldClientsCleaner.java b/torrent/src/main/java/torrent/server/OldClientsCleaner.java index d4a1023..4dfbe7f 100644 --- a/torrent/src/main/java/torrent/server/OldClientsCleaner.java +++ b/torrent/src/main/java/torrent/server/OldClientsCleaner.java @@ -19,9 +19,7 @@ public void run() { } catch (InterruptedException e) { return; } - - System.out.println("Cleaner loop"); - + long currTime = System.currentTimeMillis(); storage.lastClientUpdate @@ -30,12 +28,8 @@ public void run() { storage.clients.values().forEach(set -> set.removeIf(s -> { - boolean res = !storage.lastClientUpdate.containsKey(s); - if (res) { - System.out.println("ServerCleaner: removing " + s); - } - return res; - })); + return !storage.lastClientUpdate.containsKey(s); + })); try { storage.save(); diff --git a/torrent/src/main/java/torrent/server/UpdateHandler.java b/torrent/src/main/java/torrent/server/UpdateHandler.java index c625f29..8f5ea0b 100644 --- a/torrent/src/main/java/torrent/server/UpdateHandler.java +++ b/torrent/src/main/java/torrent/server/UpdateHandler.java @@ -39,7 +39,7 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o } storage.clients.get(id).add(clientAddr); storage.lastClientUpdate.put(clientAddr, System.currentTimeMillis()); - System.out.println("Update handler: " + clientAddr + " added"); + //System.out.println("Update handler: " + clientAddr + " added"); } storage.save(); out.writeBoolean(true); diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index f652fec..9d54f39 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -12,6 +12,7 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Random; @@ -33,6 +34,7 @@ public class InteractionTests { PrintWriter[] inp = new PrintWriter[ncl]; ByteArrayOutputStream[] outp = new ByteArrayOutputStream[ncl]; FilesHolder[] fh = new FilesHolder[ncl]; + Path root[] = new Path[ncl]; String getOutput(int i) throws InterruptedException { Thread.sleep(1000); @@ -56,7 +58,8 @@ public void initClient(int i) throws IOException { outp[i] = new ByteArrayOutputStream(); PipedInputStream pin = new PipedInputStream(); inp[i] = new PrintWriter(new PipedOutputStream(pin)); - fh[i] = new FilesHolder("torrentData/client" + i); + root[i] = Paths.get("torrentData/client" + i); + fh[i] = new FilesHolder(root[i].toString()); cl[i] = new Thread(new torrent.client.MainInner("localhost", 8090 + i, new PrintStream(outp[i]), pin, @@ -69,8 +72,8 @@ public void createClientFiles() throws IOException { Random r = new Random(5); r.nextBytes(arr); for (int i = 0; i < 2; i++) { - Files.createDirectories(Paths.get("torrentData/client" + i)); - OutputStream out = new FileOutputStream(new File("torrentData/client" + i + "/file1")); + Files.createDirectories(root[i]); + OutputStream out = new FileOutputStream(root[i].resolve("file1").toString()); out.write(arr); out.close(); } @@ -148,9 +151,68 @@ public void testBaseCommands() throws InterruptedException { assertEquals("No files yet\n>", getOutput(0)); commandTo(0, "publish nonExstFile"); - assertEquals("Failed to publish file.\n" + - " nonExstFile not found.\n" - + ">", getOutput(0)); + assertEquals("Failed to publish file.\n" + + "nonExstFile not exists.\n>", getOutput(0)); + } + + @Test + public void testDownloadFromSinglePeer() throws InterruptedException, IOException { + startServer(); + + cl[0].start(); + + commandTo(0, "publish " + root[0].resolve("file1")); + assertEquals(">The file has an id 0\n" + + ">", getOutput(0)); + + commandTo(0, "status"); + assertEquals("0 torrentData/client0/file1 complete" + + "\n>", getOutput(0)); + + + commandTo(0, "status"); + assertEquals("0 torrentData/client0/file1 complete" + + "\n>", getOutput(0)); + + commandTo(0, "list"); + assertEquals("0 file1 complete\n" + + ">", getOutput(0)); + + cl[2].start(); + assertEquals(">", getOutput(2)); + + commandTo(2, "list"); + assertEquals("0 file1 \n" + + ">", getOutput(2)); + + cl[0].interrupt(); + cl[0].join(); + + commandTo(2, "list"); + assertEquals("0 file1 \n" + + ">", getOutput(2)); + + commandTo(2, "get 0 " + root[2].resolve("file1_copy")); + assertEquals(">", getOutput(2)); + + commandTo(2, "status"); + assertEquals("0 torrentData/client2/file1_copy -> 0/2\n" + + ">", getOutput(2)); + + initClient(0); + cl[0].start(); + assertEquals(">", getOutput(0)); + + commandTo(0, "status"); + assertEquals("0 torrentData/client0/file1 complete" + + "\n>", getOutput(0)); + + String outp; + do { + commandTo(2, "status"); + outp = getOutput(2); + //System.out.println(outp); + } while (!"0 torrentData/client2/file1_copy complete\n>".equals(outp)); } } From 64214be58611ed6bb7fe4ba0483ecd525e7bc178 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 21 Dec 2018 02:52:43 +0300 Subject: [PATCH 14/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=81=202=20?= =?UTF-8?q?=D0=BF=D0=B8=D1=80=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлена команда resume. --- .../main/java/torrent/client/FilesHolder.java | 2 +- .../src/main/java/torrent/client/REPL.java | 18 +- .../test/java/torrent/InteractionTests.java | 166 ++++++++++++++---- 3 files changed, 139 insertions(+), 47 deletions(-) diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 6c86dc9..4b9da71 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -63,7 +63,7 @@ public FilesHolder(String path) throws IOException { load(); } - private void writeMaps() throws IOException { + public void writeMaps() throws IOException { if (!Files.exists(mapPath)) Files.createDirectories(mapPath); diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index 4e2e0f0..a97f753 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -147,21 +147,12 @@ void listAvaliableFiles() { + "delete \n" + "get \n" + "pause \n" + + "resume \n" + "status\n" + "help"; public void startRepl() { - /* - Options options = new Options(); - options.addOption("list", "list files, known to server"); - options.addOption("publish", true, "make file known by torrent"); - options.addOption("delete", true, "make file unknown by your client"); - options.addOption("get", true, "download file by id"); - options.addOption("pause", true, "stop file download"); - options.addOption("resume", true, "resume file downloading"); - options.addOption("status", true, "list files known by your client and their status"); - */ - + try (Scanner in = new Scanner(inStream)) { while (true) { try { @@ -197,6 +188,11 @@ public void startRepl() { downloader.stopFileDownload(in.nextInt()); } break; + case "resume": + { + downloader.startFileDownload(in.nextInt()); + } + break; case "status": { printStatus(); diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index 9d54f39..399003c 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -1,9 +1,10 @@ package torrent; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; -import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -15,6 +16,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.FileUtils; import org.junit.After; @@ -22,12 +25,13 @@ import org.junit.Test; import torrent.client.FilesHolder; +import torrent.client.FilesHolder.FileStatus; import torrent.server.MainInner; public class InteractionTests { Thread server; - + final int ncl = 3; Thread[] cl = new Thread[ncl]; @@ -35,14 +39,14 @@ public class InteractionTests { ByteArrayOutputStream[] outp = new ByteArrayOutputStream[ncl]; FilesHolder[] fh = new FilesHolder[ncl]; Path root[] = new Path[ncl]; - + String getOutput(int i) throws InterruptedException { Thread.sleep(1000); String res = outp[i].toString(); outp[i].reset(); return res; } - + void commandTo(int i, String s) { inp[i].println(s); inp[i].flush(); @@ -53,7 +57,7 @@ public void startServer() throws InterruptedException { server.start(); Thread.sleep(1000); } - + public void initClient(int i) throws IOException { outp[i] = new ByteArrayOutputStream(); PipedInputStream pin = new PipedInputStream(); @@ -66,7 +70,7 @@ public void initClient(int i) throws IOException { 2000, fh[i])); } - + public void createClientFiles() throws IOException { byte[] arr = new byte[15 * (1 << 20)]; Random r = new Random(5); @@ -96,7 +100,7 @@ public void stopServer() throws IOException, InterruptedException { server.interrupt(); server.join(); } - + public void stopClients() throws InterruptedException { for (int i = 0; i < 3; i++) { cl[i].interrupt(); @@ -115,104 +119,196 @@ public void cleanUp() throws IOException, InterruptedException { @Test public void testBaseCommands() throws InterruptedException { cl[0].start(); - + assertEquals(">", getOutput(0)); - + commandTo(0, "list"); assertEquals( "Failed to list avaliable files.\n" + - "Failed to connect to server\n" + - ">", getOutput(0)); - + "Failed to connect to server\n" + + ">", getOutput(0)); + commandTo(0, "status"); assertEquals("No files yet\n>", getOutput(0)); - + commandTo(0, "publish nonExstFile"); assertEquals("Failed to publish file.\n" + "Failed to connect to server\n>", getOutput(0)); - + commandTo(0, "pause 1"); assertEquals("This file wasn't been downloading\n>", getOutput(0)); - + commandTo(0, "delete 1"); assertEquals("File isn't known.\n>", getOutput(0)); - + commandTo(0, "get 1 torrentData/client0/file1"); assertEquals("Unknown file id. Make list first.\n>", getOutput(0)); //################################ startServer(); //################################ - + commandTo(0, "list"); assertEquals("No files avaliable on server\n>", getOutput(0)); - + commandTo(0, "status"); assertEquals("No files yet\n>", getOutput(0)); - + commandTo(0, "publish nonExstFile"); assertEquals("Failed to publish file.\n" + "nonExstFile not exists.\n>", getOutput(0)); } - + @Test public void testDownloadFromSinglePeer() throws InterruptedException, IOException { startServer(); - + cl[0].start(); - + commandTo(0, "publish " + root[0].resolve("file1")); assertEquals(">The file has an id 0\n" + ">", getOutput(0)); - + commandTo(0, "status"); assertEquals("0 torrentData/client0/file1 complete" + "\n>", getOutput(0)); - - + + commandTo(0, "status"); assertEquals("0 torrentData/client0/file1 complete" + "\n>", getOutput(0)); - + commandTo(0, "list"); assertEquals("0 file1 complete\n" + ">", getOutput(0)); - + cl[2].start(); assertEquals(">", getOutput(2)); - + commandTo(2, "list"); assertEquals("0 file1 \n" + ">", getOutput(2)); - + cl[0].interrupt(); cl[0].join(); - + commandTo(2, "list"); assertEquals("0 file1 \n" + ">", getOutput(2)); - + commandTo(2, "get 0 " + root[2].resolve("file1_copy")); assertEquals(">", getOutput(2)); - + commandTo(2, "status"); assertEquals("0 torrentData/client2/file1_copy -> 0/2\n" + ">", getOutput(2)); - + initClient(0); cl[0].start(); assertEquals(">", getOutput(0)); - + commandTo(0, "status"); assertEquals("0 torrentData/client0/file1 complete" + "\n>", getOutput(0)); + + String outp; + do { + commandTo(2, "status"); + outp = getOutput(2); + //System.out.println(outp); + } while (!"0 torrentData/client2/file1_copy complete\n>".equals(outp)); + cl[2].interrupt(); + cl[2].join(); + assertTrue(FileUtils.contentEquals(root[0].resolve("file1").toFile(), + Paths.get("torrentData/client2/file1_copy").toFile())); + } + + @Test + public void testDownloadFromTwoPeers() throws InterruptedException, FileNotFoundException, IOException { + startServer(); + + cl[0].start(); + cl[1].start(); + + commandTo(0, "publish " + root[0].resolve("file1").toString()); + assertEquals(">The file has an id 0\n" + + ">", getOutput(0)); + fh[0] + .completePieces + .get(0) + .remove(0); + fh[0].fileStatus.put(0, FileStatus.Paused); + + fh[1].filePaths.put(0, root[1].resolve("file1").toString()); + { + Set pieces = ConcurrentHashMap.newKeySet(); + pieces.add(0); + fh[1].completePieces.put(0, pieces); + } + fh[1].fileStatus.put(0, FileStatus.Paused); + fh[1].writeMaps(); + fh[1].load(); + + + cl[2].start(); + Thread.sleep(3000); + commandTo(2, "list"); + Thread.sleep(1000); + commandTo(2, "get 0 " + root[2].resolve("file1_copy")); + + getOutput(2); String outp; do { commandTo(2, "status"); outp = getOutput(2); //System.out.println(outp); + Thread.sleep(10); } while (!"0 torrentData/client2/file1_copy complete\n>".equals(outp)); + + cl[2].interrupt(); + cl[2].join(); + assertTrue(FileUtils.contentEquals(root[0].resolve("file1").toFile(), + Paths.get("torrentData/client2/file1_copy").toFile())); + } + + @Test + public void testPause() throws InterruptedException { + startServer(); + + cl[0].start(); + commandTo(0, "publish " + root[0].resolve("file1").toString()); + + getOutput(0); + + commandTo(0, "status"); + assertEquals("0 torrentData/client0/file1 complete\n>", getOutput(0)); + + commandTo(0, "pause 0"); + getOutput(0); + commandTo(0, "status"); + assertEquals("0 torrentData/client0/file1 complete\n>", getOutput(0)); + + cl[0].interrupt(); + cl[0].join(); + + cl[2].start(); + commandTo(2, "list"); + commandTo(2, "get 0 " + root[2].resolve("file1_copy")); + getOutput(2); + commandTo(2, "status"); + assertEquals("0 torrentData/client2/file1_copy -> 0/2\n>", getOutput(2)); + + commandTo(2, "pause 0"); + getOutput(2); + + commandTo(2, "status"); + assertEquals("0 torrentData/client2/file1_copy || 0/2\n>", getOutput(2)); + + commandTo(2, "resume 0"); + getOutput(2); + + commandTo(2, "status"); + assertEquals("0 torrentData/client2/file1_copy -> 0/2\n>", getOutput(2)); } - } From dad3dca086349bab7fb212182643670d36d54708 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 01:16:15 +0300 Subject: [PATCH 15/25] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=BF=D0=BE=D0=B2=D1=82=D0=BE=D1=80=D0=BD=D0=BE?= =?UTF-8?q?=D0=BC=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit До этого для него выделялся новый id. --- torrent/src/main/java/torrent/client/REPL.java | 11 ++++++++--- torrent/src/main/java/torrent/client/Uploader.java | 10 +++++----- .../src/test/java/torrent/InteractionTests.java | 14 +++++++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index a97f753..b26de80 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -5,6 +5,7 @@ import java.io.InputStream; import java.io.PrintStream; import java.net.SocketAddress; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; @@ -85,12 +86,16 @@ void deleteFile(int id) { filesHolder.deleteFile(id); } - void publishFile(String pathTo) throws FileProblemException { - pathTo = pathTo.trim(); + void publishFile(String path) throws FileProblemException { + Path pathTo = Paths.get(path.trim()); try { + if (filesHolder.filePaths.containsValue(pathTo.toString())) { + throw new FileProblemException("file with specified path used"); + } + int id = Uploader.uploadAndGetId(toServer, pathTo); - filesHolder.addExistingFile(id, Paths.get(pathTo)); + filesHolder.addExistingFile(id, pathTo); out.println("The file has an id " + id); } catch (FileNotFoundException e) { //e.printStackTrace(); diff --git a/torrent/src/main/java/torrent/client/Uploader.java b/torrent/src/main/java/torrent/client/Uploader.java index 656d049..d4b9d73 100644 --- a/torrent/src/main/java/torrent/client/Uploader.java +++ b/torrent/src/main/java/torrent/client/Uploader.java @@ -7,10 +7,10 @@ import java.net.Socket; import java.net.SocketAddress; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; public class Uploader { - public static int uploadAndGetId(SocketAddress addr, String filePath) throws IOException { + public static int uploadAndGetId(SocketAddress addr, Path filePath) throws IOException { Socket s = null; DataOutputStream dout = null; DataInputStream dinp = null; @@ -22,13 +22,13 @@ public static int uploadAndGetId(SocketAddress addr, String filePath) throws IOE dout = new DataOutputStream(s.getOutputStream()); dinp = new DataInputStream(s.getInputStream()); - if (!Files.exists(Paths.get(filePath))) { + if (!Files.exists(filePath)) { throw new FileNotFoundException(); } - long len = Paths.get(filePath).toFile().length(); + long len = filePath.toFile().length(); dout.writeByte(2); - dout.writeUTF(Paths.get(filePath).getFileName().toString()); + dout.writeUTF(filePath.getFileName().toString()); dout.writeLong(len); return dinp.readInt(); diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index 399003c..ef4cd4d 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -157,6 +157,18 @@ public void testBaseCommands() throws InterruptedException { commandTo(0, "publish nonExstFile"); assertEquals("Failed to publish file.\n" + "nonExstFile not exists.\n>", getOutput(0)); + + commandTo(0, "publish " + root[0].resolve("file1")); + getOutput(0); + commandTo(0, "list"); + assertEquals("0 file1 complete\n>", getOutput(0)); + + commandTo(0, "publish " + root[0].resolve("file1")); + assertEquals("file with specified path used\n" + + ">", getOutput(0)); + + commandTo(0, "list"); + assertEquals("0 file1 complete\n>", getOutput(0)); } @Test @@ -231,7 +243,7 @@ public void testDownloadFromTwoPeers() throws InterruptedException, FileNotFound cl[0].start(); cl[1].start(); - commandTo(0, "publish " + root[0].resolve("file1").toString()); + commandTo(0, "publish " + root[0].resolve("file1")); assertEquals(">The file has an id 0\n" + ">", getOutput(0)); fh[0] From b942e815a0fb20268889b641e3c43c13906d8968 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 20:12:21 +0300 Subject: [PATCH 16/25] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=D0=BB=20=D0=BD=D0=B0=20RandomAccessFile.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/torrent/client/FilesDownloader.java | 23 ++--- .../main/java/torrent/client/FilesHolder.java | 90 ++++++++++++------- .../main/java/torrent/client/GetHandler.java | 13 ++- .../main/java/torrent/client/MainInner.java | 1 + .../java/torrent/client/PieceDownloader.java | 12 +-- .../src/main/java/torrent/client/REPL.java | 8 +- .../test/java/torrent/InteractionTests.java | 6 +- 7 files changed, 90 insertions(+), 63 deletions(-) diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index 914db4c..db7ca28 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -1,5 +1,6 @@ package torrent.client; +import java.io.IOException; import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; @@ -10,43 +11,43 @@ import torrent.client.FilesHolder.FileStatus; public class FilesDownloader { - + FilesHolder filesHolder; SocketAddress toServer; - + ExecutorService pool = Executors.newCachedThreadPool(); - + Map> fileDownloadsFutures = new HashMap<>(); - + public FilesDownloader(FilesHolder stm, SocketAddress toServer) { this.filesHolder = stm; this.toServer = toServer; } - - public boolean startFileDownload(int fileId) { + + public boolean startFileDownload(int fileId) throws IOException { if (filesHolder.fileStatus.get(fileId) != FileStatus.Paused) { return false; } - + filesHolder.fileStatus.put(fileId, FileStatus.Downloading); - + SingleFileDownloader downloader = new SingleFileDownloader(toServer, filesHolder, fileId); fileDownloadsFutures.put(fileId, pool.submit(downloader)); return true; } - + public void stopFileDownload(int fileId) { if (!filesHolder.fileStatus.containsKey(fileId)) { throw new NullPointerException("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 index 4b9da71..ae6414d 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -1,10 +1,9 @@ package torrent.client; -import java.io.File; -import java.io.FileInputStream; +import java.io.Closeable; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -20,14 +19,14 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -public class FilesHolder { +public class FilesHolder implements Closeable{ public final int pieceSize = 0xA00000; //рабочие данные public enum FileStatus {Complete, Downloading, Paused}; - public Map files = new ConcurrentHashMap<>(); + private final Map files = new ConcurrentHashMap<>(); public Map filePaths = new ConcurrentHashMap<>(); public Map fileStatus = new ConcurrentHashMap<>(); @@ -41,7 +40,11 @@ public enum FileStatus {Complete, Downloading, Paused}; private final Path comletePiecesPath; public int numParts(int fileId) { - return (files.get(fileId).length + pieceSize - 1) / pieceSize; + try { + return Math.toIntExact((files.get(fileId).length() + pieceSize - 1) / pieceSize); + } catch (IOException e) { + throw new RuntimeException(e); + } } public int pieceOffset(int fileId, int numPart) { @@ -49,7 +52,12 @@ public int pieceOffset(int fileId, int numPart) { } public int pieceLenght(int fileId, int numPart) { - int file_length = files.get(fileId).length; + int file_length; + try { + file_length = Math.toIntExact(files.get(fileId).length()); + } catch (IOException e) { + throw new RuntimeException(e); + } return (numPart + 1) * pieceSize <= file_length ? pieceSize : file_length - numPart * pieceSize; @@ -78,25 +86,7 @@ public void writeMaps() throws IOException { } public void save() throws JsonGenerationException, JsonMappingException, IOException { - writeMaps(); - - for (Entry ent : filePaths.entrySet()) { - try (FileOutputStream fout = new FileOutputStream(new File(ent.getValue()))) { - fout.write(files.get(ent.getKey())); - } - } - } - - public void save(int fileId) throws FileNotFoundException, IOException { - - writeMaps(); - - if (files.containsKey(fileId)) { - try (FileOutputStream fout = new FileOutputStream(new File(filePaths.get(fileId)))) { - fout.write(files.get(fileId)); - } - } } public void load() throws JsonGenerationException, JsonMappingException, IOException { @@ -106,15 +96,15 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep fileStatus = mapper.readValue(fileStatusPath.toFile(), new TypeReference>() {}); if (comletePiecesPath.toFile().exists()) completePieces = mapper.readValue(comletePiecesPath.toFile(), new TypeReference>>() {}); - + if (!filePaths.keySet().equals(fileStatus.keySet()) || !filePaths.keySet().equals(completePieces.keySet())) { throw new RuntimeException("maps key sets not equal"); } for (Entry ent : filePaths.entrySet()) { - try (FileInputStream finp = new FileInputStream(new File(ent.getValue()))) { - files.put(ent.getKey(), finp.readAllBytes()); + try { + files.put(ent.getKey(), new RandomAccessFile(ent.getValue(), "rws")); } catch (FileNotFoundException fnfe) { completePieces.get(ent.getKey()).clear(); fileStatus.put(ent.getKey(), FileStatus.Downloading); @@ -136,10 +126,18 @@ public void addFileToDownload(int id, long size, String filePath) throws FilePro if (files.containsKey(id)) { throw new FileProblemException("id already used"); } + if (filePaths.containsValue(filePath)) { throw new FileProblemException("file with specified path used"); } - files.put(id, new byte[Math.toIntExact(size)]); + + try { + Paths.get(filePath).getParent().toFile().mkdirs(); + } catch (NullPointerException npe) {} + + RandomAccessFile file = new RandomAccessFile(filePath, "rws"); + file.setLength(size); + files.put(id, file); filePaths.put(id, filePath); completePieces.put(id, ConcurrentHashMap.newKeySet()); fileStatus.put(id, FileStatus.Paused); @@ -150,20 +148,46 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File if (files.containsKey(id)) { throw new FileProblemException("id already used"); } + if (filePaths.containsValue(path.toString())) { throw new FileProblemException("file with specified path used"); } - try (FileInputStream finp = new FileInputStream(path.toFile())) { - files.put(id, finp.readAllBytes()); - } + files.put(id, new RandomAccessFile(path.toFile(), "rws")); filePaths.put(id, path.toString()); fileStatus.put(id, FileStatus.Complete); completePieces.put(id, Stream.iterate(0, i -> i + 1) .limit(numParts(id)) .collect(Collectors.toSet())); - save(-1); + writeMaps(); + } + + public byte[] getPiece(int fileId, int pieceId) throws IOException { + byte[] buf = new byte[pieceLenght(fileId, pieceId)]; + files.get(fileId).seek(pieceId * pieceSize); + files.get(fileId).readFully(buf); + return buf; + } + + public void putPiece(int fileId, int pieceId, byte[] buf) throws IOException { + if (buf.length != pieceLenght(fileId, pieceId)) { + throw new RuntimeException("Attemp to put block of incorrect size"); + } + RandomAccessFile file = files.get(fileId); + file.seek(pieceId * pieceSize); + file.write(buf); + } + + @Override + public void close() throws IOException { + files.forEach((i, f) -> { + try { + f.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }); } } diff --git a/torrent/src/main/java/torrent/client/GetHandler.java b/torrent/src/main/java/torrent/client/GetHandler.java index 2379804..379bad1 100644 --- a/torrent/src/main/java/torrent/client/GetHandler.java +++ b/torrent/src/main/java/torrent/client/GetHandler.java @@ -10,9 +10,9 @@ import torrent.common.ServerRequestHandler.MessageProcessStatus; public class GetHandler implements ConcreteTaskHandler { - + private final FilesHolder fHolder; - + GetHandler(FilesHolder fHolder) { this.fHolder = fHolder; } @@ -22,16 +22,13 @@ public MessageProcessStatus computeResult(DataInputStream in, DataOutputStream o try { int id = in.readInt(); int partNum = in.readInt(); - + if (partNum >= fHolder.numParts(id)) { return MessageProcessStatus.ERROR; } - out.write( - fHolder.files.get(id), - fHolder.pieceOffset(id, partNum), - fHolder.pieceLenght(id, partNum)); - + out.write(fHolder.getPiece(id, partNum)); + return MessageProcessStatus.SUCCESS; } catch (EOFException e) { return MessageProcessStatus.INCOMPLETE; diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java index 31bb52b..72af0f1 100644 --- a/torrent/src/main/java/torrent/client/MainInner.java +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -76,6 +76,7 @@ public void run() { out.println("Saving client state..."); try { filesHolder.save(); + filesHolder.close(); } catch (IOException e1) { out.println("Saving client state failed"); out.print(e.getMessage()); diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index b6de35b..d9fab11 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -7,7 +7,7 @@ public class PieceDownloader implements CompletionHandler { private int fileId; - private int pieceOffset, pieceLength; + private int pieceLength; private int pieceIdx; private SingleFileDownloader filesDownloader; @@ -18,12 +18,11 @@ public PieceDownloader(SingleFileDownloader filesDownloader, int fileId, int pie this.fileId = fileId; this.pieceIdx = pieceIdx; - filesDownloader.filesHolder.files.get(fileId); - this.pieceOffset = filesDownloader.filesHolder.pieceOffset(fileId, pieceIdx); + filesDownloader.filesHolder.pieceOffset(fileId, pieceIdx); this.pieceLength = filesDownloader.filesHolder.pieceLenght(fileId, pieceIdx); - buffer = ByteBuffer.wrap(filesDownloader.filesHolder.files.get(fileId), pieceOffset, pieceLength); + buffer = ByteBuffer.wrap(new byte[pieceLength]); } public ByteBuffer getBuffer() { @@ -35,9 +34,10 @@ public void completed(Integer result, AsynchronousSocketChannel attachment) { if (buffer.hasRemaining()) { attachment.read(buffer, attachment, this); } else { - filesDownloader.filesHolder.completePieces.get(fileId).add(pieceIdx); - filesDownloader.checkIfComplete(); try { + filesDownloader.filesHolder.putPiece(fileId, pieceIdx, buffer.array()); + filesDownloader.filesHolder.completePieces.get(fileId).add(pieceIdx); + filesDownloader.checkIfComplete(); attachment.close(); } catch (IOException e) { // TODO Auto-generated catch block diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index b26de80..aaded03 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -37,13 +37,13 @@ public REPL( this.inStream = inStream; } - void printStatus() { + void printStatus() throws IOException { - if (filesHolder.files.isEmpty()) { + if (filesHolder.fileStatus.isEmpty()) { out.println("No files yet"); } - for (Integer id : filesHolder.files.keySet()) { + for (Integer id : filesHolder.fileStatus.keySet()) { out.print(id + " " + filesHolder.filePaths.get(id) + " "); switch (filesHolder.fileStatus.get(id)) { case Complete: @@ -157,7 +157,7 @@ void listAvaliableFiles() { + "help"; public void startRepl() { - + try (Scanner in = new Scanner(inStream)) { while (true) { try { diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index ef4cd4d..895432d 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -305,9 +305,13 @@ public void testPause() throws InterruptedException { cl[0].join(); cl[2].start(); + getOutput(2); commandTo(2, "list"); + assertEquals("0 file1 \n>", getOutput(2)); + commandTo(2, "status"); + assertEquals("No files yet\n>", getOutput(2)); commandTo(2, "get 0 " + root[2].resolve("file1_copy")); - getOutput(2); + assertEquals(">", getOutput(2)); commandTo(2, "status"); assertEquals("0 torrentData/client2/file1_copy -> 0/2\n>", getOutput(2)); From e477f21e5c4d0bfced9cfadf65f91abc17af7a7b Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 21:03:44 +0300 Subject: [PATCH 17/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=20=D0=B2=20=D1=87=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D1=8F=D1=85=20=D0=B2=20=D0=B2=D1=8B=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B=20lis?= =?UTF-8?q?t.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/src/main/java/torrent/client/REPL.java | 13 ++++++++++--- torrent/src/test/java/torrent/InteractionTests.java | 12 ++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index aaded03..f3e79ec 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -106,6 +106,10 @@ void publishFile(String path) throws FileProblemException { out.println("Failed to publish file.\n" + e.getMessage()); } } + + int getNPieces(long size) { + return Math.toIntExact((size - 1 + filesHolder.pieceSize) / filesHolder.pieceSize); + } void listAvaliableFiles() { try { @@ -117,14 +121,14 @@ void listAvaliableFiles() { for (int id : listedFilesName.keySet()) { out.print(id + " " + listedFilesName.get(id) + " "); if (!filesHolder.fileStatus.containsKey(id)) { - out.println(); + out.println(0 + "/" + getNPieces(listedFilesSize.get(id))); return; } switch (filesHolder.fileStatus.get(id)) { case Complete: { - out.println("complete"); - continue; + out.print("complete"); + break; } case Downloading: { @@ -137,6 +141,9 @@ void listAvaliableFiles() { break; } } + + out.println(" " + filesHolder.completePieces.get(id).size() + + "/" + filesHolder.numParts(id)); } } catch (IOException e) { diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index 895432d..f9f3b63 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -161,14 +161,14 @@ public void testBaseCommands() throws InterruptedException { commandTo(0, "publish " + root[0].resolve("file1")); getOutput(0); commandTo(0, "list"); - assertEquals("0 file1 complete\n>", getOutput(0)); + assertEquals("0 file1 complete 2/2\n>", getOutput(0)); commandTo(0, "publish " + root[0].resolve("file1")); assertEquals("file with specified path used\n" + ">", getOutput(0)); commandTo(0, "list"); - assertEquals("0 file1 complete\n>", getOutput(0)); + assertEquals("0 file1 complete 2/2\n>", getOutput(0)); } @Test @@ -191,21 +191,21 @@ public void testDownloadFromSinglePeer() throws InterruptedException, IOExceptio + "\n>", getOutput(0)); commandTo(0, "list"); - assertEquals("0 file1 complete\n" + + assertEquals("0 file1 complete 2/2\n" + ">", getOutput(0)); cl[2].start(); assertEquals(">", getOutput(2)); commandTo(2, "list"); - assertEquals("0 file1 \n" + + assertEquals("0 file1 0/2\n" + ">", getOutput(2)); cl[0].interrupt(); cl[0].join(); commandTo(2, "list"); - assertEquals("0 file1 \n" + + assertEquals("0 file1 0/2\n" + ">", getOutput(2)); commandTo(2, "get 0 " + root[2].resolve("file1_copy")); @@ -307,7 +307,7 @@ public void testPause() throws InterruptedException { cl[2].start(); getOutput(2); commandTo(2, "list"); - assertEquals("0 file1 \n>", getOutput(2)); + assertEquals("0 file1 0/2\n>", getOutput(2)); commandTo(2, "status"); assertEquals("No files yet\n>", getOutput(2)); commandTo(2, "get 0 " + root[2].resolve("file1_copy")); From 60fd96f08cdd46b562158542cb2e09b85be0e730 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 21:22:42 +0300 Subject: [PATCH 18/25] =?UTF-8?q?=D0=A3=D0=BD=D0=B8=D1=84=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=BA=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=20status=20=D0=B8=20list.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/torrent/client/REPL.java | 103 ++++++++---------- .../test/java/torrent/InteractionTests.java | 14 +-- 2 files changed, 51 insertions(+), 66 deletions(-) diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index f3e79ec..8d728f0 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -37,6 +37,29 @@ public REPL( this.inStream = inStream; } + void printLineForExistingFile(int id, String printName) { + out.print(id + " " + printName + " "); + switch (filesHolder.fileStatus.get(id)) { + case Complete: + { + out.print("complete"); + break; + } + case Downloading: + { + out.print("->"); + break; + } + case Paused: + { + out.print("||"); + break; + } + } + out.println(" " + filesHolder.completePieces.get(id).size() + "/" + + filesHolder.numParts(id)); + } + void printStatus() throws IOException { if (filesHolder.fileStatus.isEmpty()) { @@ -44,26 +67,28 @@ void printStatus() throws IOException { } for (Integer id : filesHolder.fileStatus.keySet()) { - out.print(id + " " + filesHolder.filePaths.get(id) + " "); - switch (filesHolder.fileStatus.get(id)) { - case Complete: - { - out.println("complete"); - continue; - } - case Downloading: - { - out.print("->"); - break; - } - case Paused: - { - out.print("||"); - break; + printLineForExistingFile(id, filesHolder.filePaths.get(id)); + } + } + + void listAvaliableFiles() { + try { + ServerFilesLister.list(toServer, listedFilesSize, listedFilesName); + + if (listedFilesName.isEmpty()) { + out.println("No files avaliable on server"); } + for (int id : listedFilesName.keySet()) { + if (!filesHolder.fileStatus.containsKey(id)) { + out.print(id + " " + listedFilesName.get(id) + " "); + out.println(0 + "/" + getNPieces(listedFilesSize.get(id))); + } else { + printLineForExistingFile(id, listedFilesName.get(id)); + } } - out.println(" " + filesHolder.completePieces.get(id).size() + "/" - + filesHolder.numParts(id)); + + } catch (IOException e) { + out.println("Failed to list avaliable files.\n" + e.getMessage()); } } @@ -106,51 +131,11 @@ void publishFile(String path) throws FileProblemException { out.println("Failed to publish file.\n" + e.getMessage()); } } - + int getNPieces(long size) { return Math.toIntExact((size - 1 + filesHolder.pieceSize) / filesHolder.pieceSize); } - void listAvaliableFiles() { - try { - ServerFilesLister.list(toServer, listedFilesSize, listedFilesName); - - if (listedFilesName.isEmpty()) { - out.println("No files avaliable on server"); - } - for (int id : listedFilesName.keySet()) { - out.print(id + " " + listedFilesName.get(id) + " "); - if (!filesHolder.fileStatus.containsKey(id)) { - out.println(0 + "/" + getNPieces(listedFilesSize.get(id))); - return; - } - switch (filesHolder.fileStatus.get(id)) { - case Complete: - { - out.print("complete"); - break; - } - case Downloading: - { - out.print("->"); - break; - } - case Paused: - { - out.print("||"); - break; - } - } - - out.println(" " + filesHolder.completePieces.get(id).size() - + "/" + filesHolder.numParts(id)); - } - - } catch (IOException e) { - out.println("Failed to list avaliable files.\n" + e.getMessage()); - } - } - final String helpMessage = "This is torrent client.\n" + "commands:\n" diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index f9f3b63..e8e9588 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -182,12 +182,12 @@ public void testDownloadFromSinglePeer() throws InterruptedException, IOExceptio ">", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete" + assertEquals("0 torrentData/client0/file1 complete 2/2" + "\n>", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete" + assertEquals("0 torrentData/client0/file1 complete 2/2" + "\n>", getOutput(0)); commandTo(0, "list"); @@ -220,7 +220,7 @@ public void testDownloadFromSinglePeer() throws InterruptedException, IOExceptio assertEquals(">", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete" + assertEquals("0 torrentData/client0/file1 complete 2/2" + "\n>", getOutput(0)); String outp; @@ -228,7 +228,7 @@ public void testDownloadFromSinglePeer() throws InterruptedException, IOExceptio commandTo(2, "status"); outp = getOutput(2); //System.out.println(outp); - } while (!"0 torrentData/client2/file1_copy complete\n>".equals(outp)); + } while (!"0 torrentData/client2/file1_copy complete 2/2\n>".equals(outp)); cl[2].interrupt(); cl[2].join(); @@ -276,7 +276,7 @@ public void testDownloadFromTwoPeers() throws InterruptedException, FileNotFound outp = getOutput(2); //System.out.println(outp); Thread.sleep(10); - } while (!"0 torrentData/client2/file1_copy complete\n>".equals(outp)); + } while (!"0 torrentData/client2/file1_copy complete 2/2\n>".equals(outp)); cl[2].interrupt(); cl[2].join(); @@ -294,12 +294,12 @@ public void testPause() throws InterruptedException { getOutput(0); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete\n>", getOutput(0)); + assertEquals("0 torrentData/client0/file1 complete 2/2\n>", getOutput(0)); commandTo(0, "pause 0"); getOutput(0); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete\n>", getOutput(0)); + assertEquals("0 torrentData/client0/file1 complete 2/2\n>", getOutput(0)); cl[0].interrupt(); cl[0].join(); From d1f5f5ba36448e58aa10afaa525f65dd809b6a66 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 21:25:49 +0300 Subject: [PATCH 19/25] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e10c59e..147f1dd 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,5 @@ mvn install В папке target появятся client.jar и server.jar . Клиенту при запуске можно передать имя хоста. По умолчанию localhost. + +У клиента есть команда `help`, показывающая список команд. \ No newline at end of file From 5b154f9b09686e6697f078208525682f65559557 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 22 Dec 2018 21:46:16 +0300 Subject: [PATCH 20/25] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BE=20=D0=B2=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B8/=D0=B2=D1=8B=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/src/main/java/torrent/client/Main.java | 2 +- torrent/src/main/java/torrent/client/MainInner.java | 2 -- torrent/src/main/java/torrent/server/Main.java | 6 ++++-- torrent/src/main/java/torrent/server/MainInner.java | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/torrent/src/main/java/torrent/client/Main.java b/torrent/src/main/java/torrent/client/Main.java index 259fb84..8eb93b0 100644 --- a/torrent/src/main/java/torrent/client/Main.java +++ b/torrent/src/main/java/torrent/client/Main.java @@ -14,7 +14,7 @@ public static void main(String[] args) throws IOException { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { - System.out.println("Saving client state..."); + System.out.println("\nSaving client state..."); t.interrupt(); } }); diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java index 72af0f1..1689fe8 100644 --- a/torrent/src/main/java/torrent/client/MainInner.java +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -69,11 +69,9 @@ public void run() { updater.join(); replTh.join(); } catch (InterruptedException e2) { - // TODO Auto-generated catch block e2.printStackTrace(); } - out.println("Saving client state..."); try { filesHolder.save(); filesHolder.close(); diff --git a/torrent/src/main/java/torrent/server/Main.java b/torrent/src/main/java/torrent/server/Main.java index 51321d9..63776d1 100644 --- a/torrent/src/main/java/torrent/server/Main.java +++ b/torrent/src/main/java/torrent/server/Main.java @@ -5,11 +5,13 @@ public static void main(String[] args) { Thread t = new Thread(new MainInner(5 * 60 * 1000)); t.start(); - + + System.out.println("Server started"); + Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { - System.out.println("Saving server state..."); + System.out.println("\nSaving server state..."); t.interrupt(); } }); diff --git a/torrent/src/main/java/torrent/server/MainInner.java b/torrent/src/main/java/torrent/server/MainInner.java index 436bf88..25ac1e9 100644 --- a/torrent/src/main/java/torrent/server/MainInner.java +++ b/torrent/src/main/java/torrent/server/MainInner.java @@ -42,7 +42,6 @@ public void run() { } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { - System.out.println("Saving server state..."); try { cleanThread.interrupt(); srvThread.interrupt(); @@ -52,7 +51,6 @@ public void run() { } catch (IOException e1) { e1.printStackTrace(); } catch (InterruptedException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } } From 483b3370cd99cc300c3cf6d106c3b82ab4ac6c4b Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 23 Dec 2018 15:39:23 +0300 Subject: [PATCH 21/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20=D1=87=D0=B5?= =?UTF-8?q?=D0=B3=D0=BE-=D1=82=D0=BE=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=20=D1=87=D0=B8=D1=81=D0=BB=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/src/main/java/torrent/client/REPL.java | 7 ++++++- torrent/src/test/java/torrent/InteractionTests.java | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/torrent/src/main/java/torrent/client/REPL.java b/torrent/src/main/java/torrent/client/REPL.java index 8d728f0..f1eaeea 100644 --- a/torrent/src/main/java/torrent/client/REPL.java +++ b/torrent/src/main/java/torrent/client/REPL.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; +import java.util.InputMismatchException; import java.util.Map; import java.util.NoSuchElementException; import java.util.Scanner; @@ -136,7 +137,7 @@ int getNPieces(long size) { return Math.toIntExact((size - 1 + filesHolder.pieceSize) / filesHolder.pieceSize); } - final String helpMessage = + public final static String helpMessage = "This is torrent client.\n" + "commands:\n" + "list\n" @@ -160,6 +161,7 @@ public void startRepl() { { out.println(helpMessage); } + break; case "list": { listAvaliableFiles(); @@ -203,6 +205,9 @@ public void startRepl() { } } catch (IOException | FileProblemException | NullPointerException e) { out.println(e.getMessage()); + } catch (InputMismatchException imme) { + in.next(); + out.println("Input mismatch"); } catch (NoSuchElementException e) { return; } diff --git a/torrent/src/test/java/torrent/InteractionTests.java b/torrent/src/test/java/torrent/InteractionTests.java index e8e9588..5b80639 100644 --- a/torrent/src/test/java/torrent/InteractionTests.java +++ b/torrent/src/test/java/torrent/InteractionTests.java @@ -26,6 +26,7 @@ import torrent.client.FilesHolder; import torrent.client.FilesHolder.FileStatus; +import torrent.client.REPL; import torrent.server.MainInner; public class InteractionTests { @@ -121,7 +122,13 @@ public void testBaseCommands() throws InterruptedException { cl[0].start(); assertEquals(">", getOutput(0)); - + + commandTo(0, "help"); + assertEquals(REPL.helpMessage + "\n>", getOutput(0)); + + commandTo(0, "delete a"); + assertEquals("Input mismatch\n>", getOutput(0)); + commandTo(0, "list"); assertEquals( "Failed to list avaliable files.\n" + From ce667b05c860e7471cd879cc2d68f1d861e26365 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 25 Dec 2018 21:43:30 +0300 Subject: [PATCH 22/25] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B2=D0=BE=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D0=BE=20=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=D0=B2=D0=B0=D1=8E=D1=89=D0=B8=D1=85=D1=81=D1=8F=20?= =?UTF-8?q?=D0=BA=D1=83=D1=81=D0=BA=D0=BE=D0=B2=20=D1=84=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/torrent/client/PieceDownloader.java | 10 ++- .../torrent/client/SingleFileDownloader.java | 68 ++++++++++++------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index d9fab11..76e8811 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; +import java.util.concurrent.Semaphore; public class PieceDownloader implements CompletionHandler { private int fileId; @@ -12,8 +13,11 @@ public class PieceDownloader implements CompletionHandler fileSources = new ArrayList<>(); private Map> pieceSources = new HashMap<>(); private Map pieceChannels = new HashMap<>(); - + + private final int numOfActivePieces = 5; + private final ExecutorService pieceQueue = Executors.newFixedThreadPool(numOfActivePieces); + private final Semaphore pieceSemaphore = new Semaphore(numOfActivePieces); + public SingleFileDownloader(SocketAddress srvAddr, FilesHolder stm, int fileId) { this.srvAddr = srvAddr; this.filesHolder = stm; @@ -129,38 +136,46 @@ private void dispatchPieceDownloaders() throws InterruptedException { if (chan != null) { if (!chan.isOpen()) { pieceChannels.remove(i); + } else { + continue; } } List sources = pieceSources.get(i); if (!sources.isEmpty()) { int rIdx = rand.nextInt(sources.size()); - - try { - chan = AsynchronousSocketChannel.open(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - try { - chan.connect(sources.get(rIdx)).get(); - } catch (ExecutionException e) { - continue; - } catch (InterruptedException e) { - return; - } - try { - if (!getRequest(chan, fileId, i)) { - System.out.println("SFD: get request failed"); - continue; + + int nPiece = i; + pieceQueue.execute(() -> { + try { + pieceSemaphore.acquire(); + } catch (InterruptedException e) { + return; } - } catch (IOException e) { - e.printStackTrace(); - continue; - } - PieceDownloader pdl = new PieceDownloader(this, fileId, i); - chan.read(pdl.getBuffer(), chan, pdl); - pieceChannels.put(i, chan); + AsynchronousSocketChannel pieceChan; + try { + pieceChan = AsynchronousSocketChannel.open(); + pieceChan.connect(sources.get(rIdx)).get(); + if (!getRequest(pieceChan, fileId, nPiece)) { + System.out.println("SFD: get request failed"); + return; + } + } catch (IOException e) { + e.printStackTrace(); + return; + } catch (InterruptedException e) { + return; + } catch (ExecutionException e) { + return; + } finally { + pieceSemaphore.release(); + } + + PieceDownloader pdl = new PieceDownloader(this, fileId, nPiece, pieceSemaphore); + pieceChan.read(pdl.getBuffer(), pieceChan, pdl); + pieceChannels.put(nPiece, pieceChan); + }); + } } } @@ -179,6 +194,7 @@ boolean checkIfComplete() { } private void closeAllChannels() { + pieceQueue.shutdownNow(); pieceChannels.forEach((i, ch) -> { try { ch.close(); From 85f1c9773aa17e9467a32bbaeeb32d42bf534ed7 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 26 Dec 2018 14:59:46 +0300 Subject: [PATCH 23/25] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В частности сделал загрузку файлового дискриптора ленивой (только когда надо считать или записать кусок). Future из мапы в FilesDownloader удаляется при завершении загрузки. --- .../java/torrent/client/FilesDownloader.java | 25 ++++-- .../main/java/torrent/client/FilesHolder.java | 84 ++++++++++++------- .../main/java/torrent/client/MainInner.java | 5 +- .../java/torrent/client/PieceDownloader.java | 21 +++-- .../src/main/java/torrent/client/REPL.java | 4 +- .../torrent/client/ServerFilesLister.java | 3 +- .../torrent/client/SingleFileDownloader.java | 6 +- .../main/java/torrent/client/Uploader.java | 3 +- .../main/java/torrent/server/MainInner.java | 1 + .../test/java/torrent/InteractionTests.java | 56 ++++++++----- 10 files changed, 132 insertions(+), 76 deletions(-) diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index db7ca28..db002a1 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -4,6 +4,7 @@ import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -12,33 +13,43 @@ public class FilesDownloader { - FilesHolder filesHolder; - SocketAddress toServer; + private FilesHolder filesHolder; + private SocketAddress toServer; - ExecutorService pool = Executors.newCachedThreadPool(); + private ExecutorService pool = Executors.newCachedThreadPool(); - Map> fileDownloadsFutures = new HashMap<>(); + 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 (filesHolder.fileStatus.get(fileId) != FileStatus.Paused) { + if (fileDownloadsFutures.containsKey(fileId)) { return false; } filesHolder.fileStatus.put(fileId, FileStatus.Downloading); - SingleFileDownloader downloader = new SingleFileDownloader(toServer, filesHolder, fileId); + 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 NullPointerException("This file wasn't been downloading"); + throw new IllegalStateException("This file wasn't been downloading"); } if (filesHolder.fileStatus.get(fileId) != FileStatus.Downloading) { diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index ae6414d..959db0c 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -31,6 +31,7 @@ public enum FileStatus {Complete, Downloading, Paused}; public Map fileStatus = new ConcurrentHashMap<>(); public Map> completePieces = new ConcurrentHashMap<>(); + public Map fileSize = new ConcurrentHashMap<>(); // ObjectMapper mapper = new ObjectMapper(); @@ -38,13 +39,10 @@ public enum FileStatus {Complete, Downloading, Paused}; private final Path filePathsPath; private final Path fileStatusPath; private final Path comletePiecesPath; + private final Path fileSizePath; public int numParts(int fileId) { - try { - return Math.toIntExact((files.get(fileId).length() + pieceSize - 1) / pieceSize); - } catch (IOException e) { - throw new RuntimeException(e); - } + return Math.toIntExact((fileSize.get(fileId) + pieceSize - 1) / pieceSize); } public int pieceOffset(int fileId, int numPart) { @@ -53,11 +51,7 @@ public int pieceOffset(int fileId, int numPart) { public int pieceLenght(int fileId, int numPart) { int file_length; - try { - file_length = Math.toIntExact(files.get(fileId).length()); - } catch (IOException e) { - throw new RuntimeException(e); - } + file_length = Math.toIntExact(fileSize.get(fileId)); return (numPart + 1) * pieceSize <= file_length ? pieceSize : file_length - numPart * pieceSize; @@ -68,6 +62,7 @@ public FilesHolder(String path) throws IOException { filePathsPath = mapPath.resolve("filepaths"); fileStatusPath = mapPath.resolve("filesStatus"); comletePiecesPath = mapPath.resolve("completePieces"); + fileSizePath = mapPath.resolve("fileSizes"); load(); } @@ -83,6 +78,9 @@ public void writeMaps() throws IOException { comletePiecesPath.toFile().createNewFile(); mapper.writeValue(comletePiecesPath.toFile(), completePieces); + + fileSizePath.toFile().createNewFile(); + mapper.writeValue(fileSizePath.toFile(), fileSize); } public void save() throws JsonGenerationException, JsonMappingException, IOException { @@ -96,16 +94,17 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep fileStatus = mapper.readValue(fileStatusPath.toFile(), new TypeReference>() {}); if (comletePiecesPath.toFile().exists()) completePieces = mapper.readValue(comletePiecesPath.toFile(), new TypeReference>>() {}); + if (fileSizePath.toFile().exists()) + fileSize = mapper.readValue(fileSizePath.toFile(), new TypeReference>() {}); if (!filePaths.keySet().equals(fileStatus.keySet()) - || !filePaths.keySet().equals(completePieces.keySet())) { + || !filePaths.keySet().equals(completePieces.keySet()) + || !filePaths.keySet().equals(fileSize.keySet())) { throw new RuntimeException("maps key sets not equal"); } - + for (Entry ent : filePaths.entrySet()) { - try { - files.put(ent.getKey(), new RandomAccessFile(ent.getValue(), "rws")); - } catch (FileNotFoundException fnfe) { + if (!Paths.get(ent.getValue()).toFile().exists()) { completePieces.get(ent.getKey()).clear(); fileStatus.put(ent.getKey(), FileStatus.Downloading); } @@ -114,12 +113,16 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep public void deleteFile(int id) { if (! files.containsKey(id)) { - throw new NullPointerException("File isn't known."); + throw new IllegalStateException("File isn't known."); } + try { + files.get(id).close(); + } catch (IOException e) {} files.remove(id); filePaths.remove(id); fileStatus.remove(id); completePieces.remove(id); + fileSize.remove(id); } public void addFileToDownload(int id, long size, String filePath) throws FileProblemException, FileNotFoundException, IOException { @@ -130,17 +133,12 @@ public void addFileToDownload(int id, long size, String filePath) throws FilePro if (filePaths.containsValue(filePath)) { throw new FileProblemException("file with specified path used"); } - - try { - Paths.get(filePath).getParent().toFile().mkdirs(); - } catch (NullPointerException npe) {} - - RandomAccessFile file = new RandomAccessFile(filePath, "rws"); - file.setLength(size); - files.put(id, file); + filePaths.put(id, filePath); completePieces.put(id, ConcurrentHashMap.newKeySet()); fileStatus.put(id, FileStatus.Paused); + fileSize.put(id, size); + writeMaps(); } @@ -153,9 +151,10 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File throw new FileProblemException("file with specified path used"); } - files.put(id, new RandomAccessFile(path.toFile(), "rws")); + fileSize.put(id, path.toFile().length()); filePaths.put(id, path.toString()); fileStatus.put(id, FileStatus.Complete); + completePieces.put(id, Stream.iterate(0, i -> i + 1) .limit(numParts(id)) @@ -163,20 +162,43 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File writeMaps(); } + private RandomAccessFile getOrCreateFile(int fileId) throws IOException { + synchronized(files) { + RandomAccessFile res = files.get(fileId); + if (res == null) { + Path parent = Paths.get(filePaths.get(fileId)).getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + res = new RandomAccessFile(filePaths.get(fileId), "rws"); + if (res.length() == 0) + res.setLength(fileSize.get(fileId)); + files.put(fileId, res); + } + + return res; + } + } + public byte[] getPiece(int fileId, int pieceId) throws IOException { byte[] buf = new byte[pieceLenght(fileId, pieceId)]; - files.get(fileId).seek(pieceId * pieceSize); - files.get(fileId).readFully(buf); + RandomAccessFile file = getOrCreateFile(fileId); + synchronized (file) { + file.seek(pieceOffset(fileId, pieceId)); + file.readFully(buf); + } return buf; } public void putPiece(int fileId, int pieceId, byte[] buf) throws IOException { if (buf.length != pieceLenght(fileId, pieceId)) { - throw new RuntimeException("Attemp to put block of incorrect size"); + throw new IllegalStateException("Attemp to put block of incorrect size"); + } + RandomAccessFile file = getOrCreateFile(fileId); + synchronized (file) { + file.seek(pieceOffset(fileId, pieceId)); + file.write(buf); } - RandomAccessFile file = files.get(fileId); - file.seek(pieceId * pieceSize); - file.write(buf); } @Override diff --git a/torrent/src/main/java/torrent/client/MainInner.java b/torrent/src/main/java/torrent/client/MainInner.java index 1689fe8..3dd1f58 100644 --- a/torrent/src/main/java/torrent/client/MainInner.java +++ b/torrent/src/main/java/torrent/client/MainInner.java @@ -48,7 +48,7 @@ public void run() { out, sinp); - replTh = new Thread(() -> repl.startRepl()); + replTh = new Thread(repl::startRepl); server.start(); updater.start(); @@ -60,6 +60,7 @@ public void run() { } catch (IOException e) { out.println(e.getMessage()); } catch (InterruptedException e) { + } finally { server.interrupt(); updater.interrupt(); replTh.interrupt(); @@ -77,7 +78,7 @@ public void run() { filesHolder.close(); } catch (IOException e1) { out.println("Saving client state failed"); - out.print(e.getMessage()); + out.print(e1.getMessage()); } } } diff --git a/torrent/src/main/java/torrent/client/PieceDownloader.java b/torrent/src/main/java/torrent/client/PieceDownloader.java index 76e8811..c293f24 100644 --- a/torrent/src/main/java/torrent/client/PieceDownloader.java +++ b/torrent/src/main/java/torrent/client/PieceDownloader.java @@ -10,7 +10,7 @@ public class PieceDownloader implements CompletionHandler fileSizes, Map", getOutput(0)); commandTo(0, "help"); @@ -168,14 +169,14 @@ public void testBaseCommands() throws InterruptedException { commandTo(0, "publish " + root[0].resolve("file1")); getOutput(0); commandTo(0, "list"); - assertEquals("0 file1 complete 2/2\n>", getOutput(0)); + assertEquals("0 file1 complete 15/15\n>", getOutput(0)); commandTo(0, "publish " + root[0].resolve("file1")); assertEquals("file with specified path used\n" + ">", getOutput(0)); commandTo(0, "list"); - assertEquals("0 file1 complete 2/2\n>", getOutput(0)); + assertEquals("0 file1 complete 15/15\n>", getOutput(0)); } @Test @@ -189,53 +190,63 @@ public void testDownloadFromSinglePeer() throws InterruptedException, IOExceptio ">", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete 2/2" + assertEquals("0 torrentData/client0/file1 complete 15/15" + "\n>", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete 2/2" + assertEquals("0 torrentData/client0/file1 complete 15/15" + "\n>", getOutput(0)); commandTo(0, "list"); - assertEquals("0 file1 complete 2/2\n" + + assertEquals("0 file1 complete 15/15\n" + ">", getOutput(0)); cl[2].start(); assertEquals(">", getOutput(2)); commandTo(2, "list"); - assertEquals("0 file1 0/2\n" + + assertEquals("0 file1 0/15\n" + ">", getOutput(2)); cl[0].interrupt(); cl[0].join(); commandTo(2, "list"); - assertEquals("0 file1 0/2\n" + + assertEquals("0 file1 0/15\n" + ">", getOutput(2)); commandTo(2, "get 0 " + root[2].resolve("file1_copy")); assertEquals(">", getOutput(2)); commandTo(2, "status"); - assertEquals("0 torrentData/client2/file1_copy -> 0/2\n" + + assertEquals("0 torrentData/client2/file1_copy -> 0/15\n" + ">", getOutput(2)); + + cl[2].interrupt(); + cl[2].join(); + initClient(2); + cl[2].start(); + getOutput(2); + commandTo(2, "status"); + assertEquals("0 torrentData/client2/file1_copy -> 0/15\n" + + ">", getOutput(2)); + initClient(0); cl[0].start(); assertEquals(">", getOutput(0)); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete 2/2" + assertEquals("0 torrentData/client0/file1 complete 15/15" + "\n>", getOutput(0)); String outp; do { commandTo(2, "status"); outp = getOutput(2); - //System.out.println(outp); - } while (!"0 torrentData/client2/file1_copy complete 2/2\n>".equals(outp)); + System.out.println(outp); + } while (!"0 torrentData/client2/file1_copy complete 15/15\n>".equals(outp)); cl[2].interrupt(); cl[2].join(); @@ -266,6 +277,7 @@ public void testDownloadFromTwoPeers() throws InterruptedException, FileNotFound fh[1].completePieces.put(0, pieces); } fh[1].fileStatus.put(0, FileStatus.Paused); + fh[1].fileSize.put(0, (long) testFileSize); fh[1].writeMaps(); fh[1].load(); @@ -281,9 +293,9 @@ public void testDownloadFromTwoPeers() throws InterruptedException, FileNotFound do { commandTo(2, "status"); outp = getOutput(2); - //System.out.println(outp); + System.out.println(outp); Thread.sleep(10); - } while (!"0 torrentData/client2/file1_copy complete 2/2\n>".equals(outp)); + } while (!"0 torrentData/client2/file1_copy complete 15/15\n>".equals(outp)); cl[2].interrupt(); cl[2].join(); @@ -301,12 +313,12 @@ public void testPause() throws InterruptedException { getOutput(0); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete 2/2\n>", getOutput(0)); + assertEquals("0 torrentData/client0/file1 complete 15/15\n>", getOutput(0)); commandTo(0, "pause 0"); getOutput(0); commandTo(0, "status"); - assertEquals("0 torrentData/client0/file1 complete 2/2\n>", getOutput(0)); + assertEquals("0 torrentData/client0/file1 complete 15/15\n>", getOutput(0)); cl[0].interrupt(); cl[0].join(); @@ -314,24 +326,24 @@ public void testPause() throws InterruptedException { cl[2].start(); getOutput(2); commandTo(2, "list"); - assertEquals("0 file1 0/2\n>", getOutput(2)); + assertEquals("0 file1 0/15\n>", getOutput(2)); commandTo(2, "status"); assertEquals("No files yet\n>", getOutput(2)); commandTo(2, "get 0 " + root[2].resolve("file1_copy")); assertEquals(">", getOutput(2)); commandTo(2, "status"); - assertEquals("0 torrentData/client2/file1_copy -> 0/2\n>", getOutput(2)); + assertEquals("0 torrentData/client2/file1_copy -> 0/15\n>", getOutput(2)); commandTo(2, "pause 0"); getOutput(2); commandTo(2, "status"); - assertEquals("0 torrentData/client2/file1_copy || 0/2\n>", getOutput(2)); + assertEquals("0 torrentData/client2/file1_copy || 0/15\n>", getOutput(2)); commandTo(2, "resume 0"); getOutput(2); commandTo(2, "status"); - assertEquals("0 torrentData/client2/file1_copy -> 0/2\n>", getOutput(2)); + assertEquals("0 torrentData/client2/file1_copy -> 0/15\n>", getOutput(2)); } } From 92a22e1e68af75259ee5adc5c6f60329690acf22 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 26 Dec 2018 16:42:58 +0300 Subject: [PATCH 24/25] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20do?= =?UTF-8?q?uble=20check=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20synchroni?= =?UTF-8?q?zed=20=D0=B2=20getOrCreateFile=20=D0=B2=20FilesHolder.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправил catch NPE --- .../java/torrent/client/FilesDownloader.java | 1 - .../main/java/torrent/client/FilesHolder.java | 31 ++++++++++--------- .../torrent/client/ServerFilesLister.java | 14 ++++++--- .../main/java/torrent/client/Uploader.java | 18 ++++++----- .../test/java/torrent/InteractionTests.java | 6 ++-- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/torrent/src/main/java/torrent/client/FilesDownloader.java b/torrent/src/main/java/torrent/client/FilesDownloader.java index db002a1..3a81e08 100644 --- a/torrent/src/main/java/torrent/client/FilesDownloader.java +++ b/torrent/src/main/java/torrent/client/FilesDownloader.java @@ -4,7 +4,6 @@ import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; -import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; diff --git a/torrent/src/main/java/torrent/client/FilesHolder.java b/torrent/src/main/java/torrent/client/FilesHolder.java index 959db0c..e21f102 100644 --- a/torrent/src/main/java/torrent/client/FilesHolder.java +++ b/torrent/src/main/java/torrent/client/FilesHolder.java @@ -102,7 +102,7 @@ public void load() throws JsonGenerationException, JsonMappingException, IOExcep || !filePaths.keySet().equals(fileSize.keySet())) { throw new RuntimeException("maps key sets not equal"); } - + for (Entry ent : filePaths.entrySet()) { if (!Paths.get(ent.getValue()).toFile().exists()) { completePieces.get(ent.getKey()).clear(); @@ -133,12 +133,12 @@ public void addFileToDownload(int id, long size, String filePath) throws FilePro if (filePaths.containsValue(filePath)) { throw new FileProblemException("file with specified path used"); } - + filePaths.put(id, filePath); completePieces.put(id, ConcurrentHashMap.newKeySet()); fileStatus.put(id, FileStatus.Paused); fileSize.put(id, size); - + writeMaps(); } @@ -163,21 +163,22 @@ public void addExistingFile(int id, Path path) throws FileProblemException, File } private RandomAccessFile getOrCreateFile(int fileId) throws IOException { - synchronized(files) { - RandomAccessFile res = files.get(fileId); - if (res == null) { - Path parent = Paths.get(filePaths.get(fileId)).getParent(); - if (parent != null) { - Files.createDirectories(parent); + if (!files.containsKey(fileId)) { + synchronized (files) { + if (!files.containsKey(fileId)) { + Path parent = Paths.get(filePaths.get(fileId)).getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + RandomAccessFile res_local = new RandomAccessFile(filePaths.get(fileId), "rws"); + if (res_local.length() == 0) + res_local.setLength(fileSize.get(fileId)); + files.put(fileId, res_local); } - res = new RandomAccessFile(filePaths.get(fileId), "rws"); - if (res.length() == 0) - res.setLength(fileSize.get(fileId)); - files.put(fileId, res); } - - return res; } + + return files.get(fileId); } public byte[] getPiece(int fileId, int pieceId) throws IOException { diff --git a/torrent/src/main/java/torrent/client/ServerFilesLister.java b/torrent/src/main/java/torrent/client/ServerFilesLister.java index 5f816f9..a9b4c48 100644 --- a/torrent/src/main/java/torrent/client/ServerFilesLister.java +++ b/torrent/src/main/java/torrent/client/ServerFilesLister.java @@ -20,7 +20,7 @@ public static void list(SocketAddress addr, Map fileSizes, Map fileSizes, MapThe file has an id 0\n" + ">", getOutput(0)); @@ -309,8 +310,9 @@ public void testPause() throws InterruptedException { cl[0].start(); commandTo(0, "publish " + root[0].resolve("file1").toString()); - - getOutput(0); + Thread.sleep(1000); + assertEquals(">The file has an id 0\n" + + ">", getOutput(0)); commandTo(0, "status"); assertEquals("0 torrentData/client0/file1 complete 15/15\n>", getOutput(0)); From 7dd71bbd78e92e4d7f2b788f39d4d476328fa58c Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 27 Dec 2018 14:51:37 +0300 Subject: [PATCH 25/25] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20DataStream?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=D0=B2=D1=8F=D0=B7=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BA=20=D1=81=D0=BE=D0=BA=D0=B5=D1=82=D1=83.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- torrent/src/main/java/torrent/client/ServerFilesLister.java | 4 ---- torrent/src/main/java/torrent/client/Uploader.java | 4 ---- 2 files changed, 8 deletions(-) diff --git a/torrent/src/main/java/torrent/client/ServerFilesLister.java b/torrent/src/main/java/torrent/client/ServerFilesLister.java index a9b4c48..31c9536 100644 --- a/torrent/src/main/java/torrent/client/ServerFilesLister.java +++ b/torrent/src/main/java/torrent/client/ServerFilesLister.java @@ -40,10 +40,6 @@ public static void list(SocketAddress addr, Map fileSizes, Map