From 059c627b29d9ca97aaaf35340597e98168102e72 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Fri, 8 May 2020 14:25:09 +0200 Subject: [PATCH] hw2: done --- hw2/.idea/misc.xml | 2 +- hw2/ciao.txt | 113 +++++++++++++++++++++++ hw2/mamma.txt | 113 +++++++++++++++++++++++ hw2/src/{ => impl}/GBNTReceiver.java | 40 ++++++--- hw2/src/{ => impl}/GBNTSender.java | 57 +++++++++--- hw2/src/transport/FileReceiver.java | 42 +++++++++ hw2/src/transport/FileSender.java | 53 +++++++++++ hw2/src/transport/MyTimer.java | 81 +++++++++++++++++ hw2/src/transport/Receiver.java | 65 ++++++++++++++ hw2/src/transport/ScheduledAction.java | 11 +++ hw2/src/transport/Sender.java | 34 +++++++ hw2/src/transport/TimeoutAction.java | 5 ++ hw2/src/transport/Transport.java | 118 +++++++++++++++++++++++++ hw2/transport.jar | Bin 8903 -> 0 bytes 14 files changed, 709 insertions(+), 25 deletions(-) create mode 100644 hw2/ciao.txt create mode 100644 hw2/mamma.txt rename hw2/src/{ => impl}/GBNTReceiver.java (55%) rename hw2/src/{ => impl}/GBNTSender.java (69%) create mode 100644 hw2/src/transport/FileReceiver.java create mode 100644 hw2/src/transport/FileSender.java create mode 100644 hw2/src/transport/MyTimer.java create mode 100644 hw2/src/transport/Receiver.java create mode 100644 hw2/src/transport/ScheduledAction.java create mode 100644 hw2/src/transport/Sender.java create mode 100644 hw2/src/transport/TimeoutAction.java create mode 100644 hw2/src/transport/Transport.java delete mode 100644 hw2/transport.jar diff --git a/hw2/.idea/misc.xml b/hw2/.idea/misc.xml index 37e641e..8f5f4fa 100644 --- a/hw2/.idea/misc.xml +++ b/hw2/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/hw2/ciao.txt b/hw2/ciao.txt new file mode 100644 index 0000000..a4e075b --- /dev/null +++ b/hw2/ciao.txt @@ -0,0 +1,113 @@ + Siena (Scalable Internet Event Notification Architectures) is a research project aimed at + designing and constructing a generic scalable publish/subscribe event-notification service. + The technical basis of Siena is an innovative type of network service called content-based networking. + +Overview +The asynchrony, heterogeneity, and inherent loose coupling that characterize applications in a wide-area + network promote event interaction as a natural design abstraction for a growing class of software systems. + An emerging building block for such systems is an infrastructure called publish/subscribe event notification + service. + +Software systems of a significant dimension, especially those that are distributed over a computer network, +are often engineered by means of the integration of components. A promising approach to support component-based +software architectures is the so-called event-based style whereby the interaction of components is modeled with +events. Components emit events to inform other components of a change in their internal state or to request services +from other components. Upon detecting the occurrence of events, components react by executing some actions and possibly + emitting other events. The glue that ties components together in an event-based architecture is an infrastructure that + we call event service. The event service registers the interests of components and then dispatches event notifications + accordingly. The advantage of using an event service instead of other "classical" integration mechanisms + such as direct or remote invocation is that this method increases the degree of de-coupling among components + thus eliminating static dependencies and improving interoperability. + +We envision a ubiquitous event notification service accessible from every site on a wide-area network and suitable +for supporting highly distributed applications requiring component interactions ranging in granularity from fine to + coarse. Conceptually, the service is implemented as a network of servers that provide access points to clients. Clients + use the access points to advertise the information about events that they generate and to publish notifications + containing that information. They also use the access points to subscribe for notifications of interest. + The service uses the access points to then notify clients by delivering any notifications of interest. Clearly, an + event notification service complements other general-purpose middleware services, such as point-to-point and multicast + communication mechanisms, by offering a many-to-many communication and integration facility. + +Given that the primary purpose of an event notification service is to support notification selection and delivery, + the challenge we face in a wide-area setting is maximizing expressiveness in the selection mechanism without sacrificing + scalability in the delivery mechanism. Expressiveness refers to the ability of the event notification service to provide + a powerful data model with which to capture information about events, to express filters and patterns on notifications + of interest, and to use that data model as the basis for optimizing notification delivery. In terms of scalability, + we are referring not simply to the number of event generators, the number of event notifications, and the number + of notification recipients, but also to the need to discard many of the assumptions made for local-area networks, + such as low latency, abundant bandwidth, homogeneous platforms, continuous and reliable connectivity, and + centralized control. + + +Intuitively, a simple event notification service that provides no selection mechanism can be reduced to a multicast +routing and transport mechanism for which there are numerous scalable implementations. However, once the service provides + a selection mechanism, then the overall efficiency of the service and its routing of notifications are affected by the + power of the language used to construct notifications and to express filters and patterns. As the power of the language + increases, so does the complexity of the processing. Thus, in practice, scalability and expressiveness are two + conflicting goals that must be traded off. + +Siena is an event notification service that we have designed and implemented to maximize both expressiveness and +scalability. A prototype implementation of Siena is available. +People +Siena is primarily the work of Antonio Carzaniga and Alexander L. Wolf. D.S. Rosenblum also made significant +contributions to the initial design of Siena. + +Others also contributed directly to the development of Siena. Among them are John Giacomoni, Mauro Caporuscio, +Matthew J. Rutherford, Cyrus P. Hall, Yanyan Wang, Giovanni Toffetti, and Amir Malekpour. +Documents +Articles are available in Portable Document Format (PDF) or PostScript® format and some of them are compressed +with gzip. Downloading any one of these documents indicates that you agree to abide by a copyright notice. +Selected Papers + + Design and Evaluation of a Wide-Area Event Notification Service + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + ACM Transactions on Computer Systems, 19(3):332-383, August 2001. + Abstract, BibTeX ref., PDF, DOI + End-to-End Reliability for Best-Effort Content-Based Publish/Subscribe Networks + A. Malekpour, A. Carzaniga, F. Pedone, and G. Toffetti Carughi + In Proceedings of the 5th ACM International Conference on Distributed Event-Based Systems (DEBS 2011). New York, New York, July 2011. + Abstract, BibTeX ref., PDF, DOI + A Routing Scheme for Content-Based Networking + A. Carzaniga, M.J. Rutherford, and A.L. Wolf + Proceedings of IEEE INFOCOM 2004. Hong Kong, China. March 2004. + Abstract, BibTeX ref., PDF, DOI + Forwarding in a Content-Based Network + A. Carzaniga and A.L. Wolf + Proceedings of ACM SIGCOMM 2003. p. 163-174. Karlsruhe, Germany. August 2003. + Abstract, BibTeX ref., PDF, DOI + Design and Evaluation of a Support Service for Mobile, Wireless Publish/Subscribe Applications + M. Caporuscio, A. Carzaniga, and A.L. Wolf + IEEE Transactions on Software Engineering, 29(12):1059-1071, December 2003. + Abstract, BibTeX ref., PDF, DOI + Content-based Networking: A New Communication Infrastructure + A. Carzaniga and A.L. Wolf + In NSF Workshop on an Infrastructure for Mobile and Wireless Systems. Lecture Notes in Computer Science n. 2538 p. 59-68. Springer-Verlag. Scottsdale, Arizona. October 2001. + Abstract, BibTeX ref., PDF + Achieving Expressiveness and Scalability in an Internet-Scale Event Notification Service + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + Nineteenth ACM Symposium on Principles of Distributed Computing (PODC2000). Portland, Oregon, July 2000. + Abstract, BibTeX ref., PDF, DOI + Content-Based Addressing and Routing: A General Model and its Application + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + Technical Report CU-CS-902-00, Department of Computer Science, University of Colorado, January 2000. + Abstract, BibTeX ref., PDF + Architectures for an Event Notification Service Scalable to Wide-area Networks + A. Carzaniga + PhD Thesis. Politecnico di Milano. December, 1998. + Abstract, BibTeX ref., PDF + A Design Framework for Internet-Scale Event Observation and Notification + D.S. Rosenblum and A.L. Wolf + 6th European Software Engineering Conference. Lecture Notes in Computer Science 1301, Springer, Berlin, 1997. + +Acknowledgments +This work was supported in part by the Air Force Materiel Command, Rome Laboratory, and the Defense Advanced +Research Projects Agency under Contract Numbers F30602-94-C-0253, F30602-97-2-0021, F30602-98-2-0163, +and F30602-99-C-0174; by the Air Force Office of Scientific Research, Air Force Materiel Command, USAF, u +nder grant number F49620-98-1-0061; and by the National Science Foundation under Grant Number CCR-9701973. +The content of the information does not necessarily reflect the position or the policy of the US Governmen +t and no official endorsement should be inferred. + +We thank Dennis Heimbigner, Richard S. Hall, and André van der Hoek of the Software Engineering Research Laboratory, +University of Colorado at Boulder (circa 1997), and Giampaolo Cugola, Elisabetta Di Nitto, and Alfonso Fuggetta of + Politecnico di Milano for their contributions to this work. +this page is maintained by Antonio Carzaniga and was updated on September 15, 2019 \ No newline at end of file diff --git a/hw2/mamma.txt b/hw2/mamma.txt new file mode 100644 index 0000000..a4e075b --- /dev/null +++ b/hw2/mamma.txt @@ -0,0 +1,113 @@ + Siena (Scalable Internet Event Notification Architectures) is a research project aimed at + designing and constructing a generic scalable publish/subscribe event-notification service. + The technical basis of Siena is an innovative type of network service called content-based networking. + +Overview +The asynchrony, heterogeneity, and inherent loose coupling that characterize applications in a wide-area + network promote event interaction as a natural design abstraction for a growing class of software systems. + An emerging building block for such systems is an infrastructure called publish/subscribe event notification + service. + +Software systems of a significant dimension, especially those that are distributed over a computer network, +are often engineered by means of the integration of components. A promising approach to support component-based +software architectures is the so-called event-based style whereby the interaction of components is modeled with +events. Components emit events to inform other components of a change in their internal state or to request services +from other components. Upon detecting the occurrence of events, components react by executing some actions and possibly + emitting other events. The glue that ties components together in an event-based architecture is an infrastructure that + we call event service. The event service registers the interests of components and then dispatches event notifications + accordingly. The advantage of using an event service instead of other "classical" integration mechanisms + such as direct or remote invocation is that this method increases the degree of de-coupling among components + thus eliminating static dependencies and improving interoperability. + +We envision a ubiquitous event notification service accessible from every site on a wide-area network and suitable +for supporting highly distributed applications requiring component interactions ranging in granularity from fine to + coarse. Conceptually, the service is implemented as a network of servers that provide access points to clients. Clients + use the access points to advertise the information about events that they generate and to publish notifications + containing that information. They also use the access points to subscribe for notifications of interest. + The service uses the access points to then notify clients by delivering any notifications of interest. Clearly, an + event notification service complements other general-purpose middleware services, such as point-to-point and multicast + communication mechanisms, by offering a many-to-many communication and integration facility. + +Given that the primary purpose of an event notification service is to support notification selection and delivery, + the challenge we face in a wide-area setting is maximizing expressiveness in the selection mechanism without sacrificing + scalability in the delivery mechanism. Expressiveness refers to the ability of the event notification service to provide + a powerful data model with which to capture information about events, to express filters and patterns on notifications + of interest, and to use that data model as the basis for optimizing notification delivery. In terms of scalability, + we are referring not simply to the number of event generators, the number of event notifications, and the number + of notification recipients, but also to the need to discard many of the assumptions made for local-area networks, + such as low latency, abundant bandwidth, homogeneous platforms, continuous and reliable connectivity, and + centralized control. + + +Intuitively, a simple event notification service that provides no selection mechanism can be reduced to a multicast +routing and transport mechanism for which there are numerous scalable implementations. However, once the service provides + a selection mechanism, then the overall efficiency of the service and its routing of notifications are affected by the + power of the language used to construct notifications and to express filters and patterns. As the power of the language + increases, so does the complexity of the processing. Thus, in practice, scalability and expressiveness are two + conflicting goals that must be traded off. + +Siena is an event notification service that we have designed and implemented to maximize both expressiveness and +scalability. A prototype implementation of Siena is available. +People +Siena is primarily the work of Antonio Carzaniga and Alexander L. Wolf. D.S. Rosenblum also made significant +contributions to the initial design of Siena. + +Others also contributed directly to the development of Siena. Among them are John Giacomoni, Mauro Caporuscio, +Matthew J. Rutherford, Cyrus P. Hall, Yanyan Wang, Giovanni Toffetti, and Amir Malekpour. +Documents +Articles are available in Portable Document Format (PDF) or PostScript® format and some of them are compressed +with gzip. Downloading any one of these documents indicates that you agree to abide by a copyright notice. +Selected Papers + + Design and Evaluation of a Wide-Area Event Notification Service + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + ACM Transactions on Computer Systems, 19(3):332-383, August 2001. + Abstract, BibTeX ref., PDF, DOI + End-to-End Reliability for Best-Effort Content-Based Publish/Subscribe Networks + A. Malekpour, A. Carzaniga, F. Pedone, and G. Toffetti Carughi + In Proceedings of the 5th ACM International Conference on Distributed Event-Based Systems (DEBS 2011). New York, New York, July 2011. + Abstract, BibTeX ref., PDF, DOI + A Routing Scheme for Content-Based Networking + A. Carzaniga, M.J. Rutherford, and A.L. Wolf + Proceedings of IEEE INFOCOM 2004. Hong Kong, China. March 2004. + Abstract, BibTeX ref., PDF, DOI + Forwarding in a Content-Based Network + A. Carzaniga and A.L. Wolf + Proceedings of ACM SIGCOMM 2003. p. 163-174. Karlsruhe, Germany. August 2003. + Abstract, BibTeX ref., PDF, DOI + Design and Evaluation of a Support Service for Mobile, Wireless Publish/Subscribe Applications + M. Caporuscio, A. Carzaniga, and A.L. Wolf + IEEE Transactions on Software Engineering, 29(12):1059-1071, December 2003. + Abstract, BibTeX ref., PDF, DOI + Content-based Networking: A New Communication Infrastructure + A. Carzaniga and A.L. Wolf + In NSF Workshop on an Infrastructure for Mobile and Wireless Systems. Lecture Notes in Computer Science n. 2538 p. 59-68. Springer-Verlag. Scottsdale, Arizona. October 2001. + Abstract, BibTeX ref., PDF + Achieving Expressiveness and Scalability in an Internet-Scale Event Notification Service + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + Nineteenth ACM Symposium on Principles of Distributed Computing (PODC2000). Portland, Oregon, July 2000. + Abstract, BibTeX ref., PDF, DOI + Content-Based Addressing and Routing: A General Model and its Application + A. Carzaniga, D.S. Rosenblum, and A.L. Wolf + Technical Report CU-CS-902-00, Department of Computer Science, University of Colorado, January 2000. + Abstract, BibTeX ref., PDF + Architectures for an Event Notification Service Scalable to Wide-area Networks + A. Carzaniga + PhD Thesis. Politecnico di Milano. December, 1998. + Abstract, BibTeX ref., PDF + A Design Framework for Internet-Scale Event Observation and Notification + D.S. Rosenblum and A.L. Wolf + 6th European Software Engineering Conference. Lecture Notes in Computer Science 1301, Springer, Berlin, 1997. + +Acknowledgments +This work was supported in part by the Air Force Materiel Command, Rome Laboratory, and the Defense Advanced +Research Projects Agency under Contract Numbers F30602-94-C-0253, F30602-97-2-0021, F30602-98-2-0163, +and F30602-99-C-0174; by the Air Force Office of Scientific Research, Air Force Materiel Command, USAF, u +nder grant number F49620-98-1-0061; and by the National Science Foundation under Grant Number CCR-9701973. +The content of the information does not necessarily reflect the position or the policy of the US Governmen +t and no official endorsement should be inferred. + +We thank Dennis Heimbigner, Richard S. Hall, and André van der Hoek of the Software Engineering Research Laboratory, +University of Colorado at Boulder (circa 1997), and Giampaolo Cugola, Elisabetta Di Nitto, and Alfonso Fuggetta of + Politecnico di Milano for their contributions to this work. +this page is maintained by Antonio Carzaniga and was updated on September 15, 2019 \ No newline at end of file diff --git a/hw2/src/GBNTReceiver.java b/hw2/src/impl/GBNTReceiver.java similarity index 55% rename from hw2/src/GBNTReceiver.java rename to hw2/src/impl/GBNTReceiver.java index 363f781..a45f83d 100644 --- a/hw2/src/GBNTReceiver.java +++ b/hw2/src/impl/GBNTReceiver.java @@ -1,3 +1,5 @@ +package impl; + import transport.Receiver; import transport.TimeoutAction; @@ -5,12 +7,13 @@ public class GBNTReceiver extends Receiver implements TimeoutAction { private char sequence; boolean firstPacket = true; - private static final int RECEIVER_TIMEOUT_MS = 1000; + private static final int RECEIVER_TIMEOUT_MS = 5000; private State state = State.SETUP; enum State { SETUP, - RECEIVING + RECEIVING, + CLOSED, } private static byte[] generateACK(char sequence) { @@ -21,7 +24,7 @@ public class GBNTReceiver extends Receiver implements TimeoutAction { } private static char readBigEndianChar(byte[] src, int offset) { - return (char) (src[offset] << 8 + src[offset + 1]); + return (char) (((src[offset] << 8) & 0xFF00) | (src[offset + 1] & 0xFF)); } private static char incrementAndRollover(char c) { @@ -30,8 +33,11 @@ public class GBNTReceiver extends Receiver implements TimeoutAction { @Override public void timeoutAction() { + System.out.println("Disconnect timeout triggered"); try { disconnect(); + deliver(null, 0, END_OF_STREAM); + state = State.CLOSED; } catch (java.lang.InterruptedException ex) { System.out.println("Thread interrupted. Exiting directly."); System.exit(0); @@ -40,31 +46,45 @@ public class GBNTReceiver extends Receiver implements TimeoutAction { @Override protected void unreliableReceive(byte[] bytes, int offset, int length) { + if (state == State.CLOSED) return; + //System.out.println("State: " + state + " receiving: " + length); + cancelTimeout(this); + setTimeout(RECEIVER_TIMEOUT_MS, this); switch(this.state) { case SETUP: if (length != 2) { - if (firstPacket) return; - else { + if (!firstPacket) { state = State.RECEIVING; this.unreliableReceive(bytes, offset, length); + return; } + return; } - sequence = readBigEndianChar(bytes, 0); + sequence = readBigEndianChar(bytes, offset); + System.out.println("Synchronization ACK: " + (int) sequence); unreliableSend(bytes, offset, 2); firstPacket = false; break; case RECEIVING: if (length == 2) { // if packet is a close packet - if (incrementAndRollover(this.sequence) != readBigEndianChar(bytes, offset)) return; // ignore if not synchronized + char a = readBigEndianChar(bytes, offset); + if (incrementAndRollover(this.sequence) != a) return; // ignore if not synchronized this.unreliableSend(bytes, offset, 2); + System.out.println("Received valid FIN packet"); timeoutAction(); } if (length < 3) return; - char seq = readBigEndianChar(bytes, 0); - if (seq != incrementAndRollover(sequence)) return; //drop the packet + char seq = readBigEndianChar(bytes, offset); + System.out.print("Read sequence: " + (int) seq + " -> "); + + if (seq != incrementAndRollover(sequence)) { + System.out.println("Dropping"); + return; + } //drop the packet else { - this.unreliableReceive(bytes, offset + 2, length); + this.deliver(bytes, offset + 2, length - 2); sequence = incrementAndRollover(sequence); + System.out.println("Sending ack: "+ (int) sequence); this.unreliableSend(generateACK(sequence), 0, 2); } break; diff --git a/hw2/src/GBNTSender.java b/hw2/src/impl/GBNTSender.java similarity index 69% rename from hw2/src/GBNTSender.java rename to hw2/src/impl/GBNTSender.java index 612c26c..72192a8 100644 --- a/hw2/src/GBNTSender.java +++ b/hw2/src/impl/GBNTSender.java @@ -1,6 +1,9 @@ +package impl; + import transport.TimeoutAction; import java.util.*; +import java.util.concurrent.Semaphore; public class GBNTSender extends transport.Sender implements TimeoutAction { @@ -10,7 +13,7 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { } private static char readBigEndianChar(byte[] src, int offset) { - return (char) (src[offset] << 8 + src[offset + 1]); + return (char) (((src[offset] << 8) & 0xFF00) | (src[offset + 1] & 0xFF)); } private static char addAndRollover(char c, char i) { @@ -22,6 +25,9 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { } private final Map packetMap = new HashMap<>(); + private Semaphore s = new Semaphore(0); + + private static final int MAX_PACKET_SIZE = 128; private class Packet { private final byte[] contents; @@ -31,16 +37,16 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { Packet(char sequence, byte[] buffer, int offset, int length) { this.sequence = sequence; - this.length = Math.min(length + 2, MAX_PACKET_SIZE); - contents = new byte[MAX_PACKET_SIZE]; + this.length = Math.min(length, MAX_PACKET_SIZE - 2); + contents = new byte[this.length + 2]; writeBigEndianChar(contents, sequence); - System.arraycopy(buffer, offset, this.contents, 0, this.length); + System.arraycopy(buffer, offset + 2 - 2, contents, 2, contents.length - 2); packetMap.put(this.sequence, this); } void send() { estimateStart = System.currentTimeMillis(); - unreliableSend(contents, 0, length); + unreliableSend(contents, 0, contents.length); } void destroy() { @@ -62,8 +68,8 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { private char base = (char) (new Random().nextInt() % 65536); private char waitingACK = 0; private int timeoutMs = 500; - private double rttEWMA = 0; - private double devRttEWMA = 0; + private double rttEWMA = 100; + private double devRttEWMA = 100; private State state = State.SETUP; private final Queue packets = new ArrayDeque<>(); @@ -74,14 +80,11 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { CLOSING, } - public GBNTSender() { - sendSetup(); - } - private void computeTimeoutLength(long rttMIllis) { devRttEWMA = 0.75 * devRttEWMA + 0.25 * (rttEWMA - rttMIllis); rttEWMA = 0.875 * rttEWMA + 0.125 * rttMIllis; timeoutMs = (int) (rttEWMA + 4 * devRttEWMA); + //System.out.println("devRTT: " + devRttEWMA + " RTT: " + rttEWMA + " timeout: " + timeoutMs); } private void flushAckedPackets() { @@ -109,15 +112,20 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { @Override protected int reliableSend(byte[] bytes, int offset, int length) { - if (state != State.SENDING) throw new IllegalStateException(); + if (state == State.SETUP) { + sendSetup(); + return 0; + } Packet p = new Packet(addAndRollover(base, waitingACK), bytes, offset, length); waitingACK++; packets.add(p); + System.out.println("Sending seq: " + (int) p.sequence + " - (" + p.length + " + 2 bytes)"); p.send(); setTimeout(timeoutMs, this); if (waitingACK >= W) { + System.out.println("Window full " + (int) p.sequence); blockSender(); } @@ -127,16 +135,27 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { @Override public void close() { state = State.CLOSING; + System.out.print("FIN packet: "); + reliableSend(new byte[0], 0, 0); if (waitingACK > 0) { blockSender(); + try { + s.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(255); + } } } @Override public void timeoutAction() { + if (state == State.SENDING) for (Packet p : packets) { p.send(); } + else if (state == State.SETUP) + sendSetup(); } @Override @@ -147,24 +166,34 @@ public class GBNTSender extends transport.Sender implements TimeoutAction { if (bytes[offset] == expected[0] && bytes[1] == expected[offset + 1]) { state = State.SENDING; base = incrementAndRollover(base); + System.out.println("Synchronization successful"); resumeSender(); } else { + System.out.println("Synchronization seq: " + (int) base); sendSetup(); } break; case SENDING: case CLOSING: char ackSeq = readBigEndianChar(bytes, offset); + System.out.println("Received ACK: " + (int) ackSeq); cancelTimeout(this); - computeTimeoutLength(packetMap.get(ackSeq).getRTT()); + Packet p = packetMap.get(ackSeq); + if (p != null) computeTimeoutLength(p.getRTT()); boolean mustResume = waitingACK == W && state == State.SENDING; - waitingACK -= (ackSeq - base); + + waitingACK -= (ackSeq + 1 - base); base = incrementAndRollover(ackSeq); flushAckedPackets(); + if (mustResume || (waitingACK == 0 && state == State.CLOSING)) { resumeSender(); + if (state == State.CLOSING) { + System.out.println("Closing"); + s.release(); + } } } } diff --git a/hw2/src/transport/FileReceiver.java b/hw2/src/transport/FileReceiver.java new file mode 100644 index 0000000..5e18e31 --- /dev/null +++ b/hw2/src/transport/FileReceiver.java @@ -0,0 +1,42 @@ +package transport; + +import impl.GBNTReceiver; + +import java.io.FileOutputStream; +import java.net.InetAddress; + +public class FileReceiver { + + public static void main(String[] var0) throws Exception { + int var1 = 0; + switch(var0.length) { + case 6: + var1 = Integer.parseInt(var0[5]); + case 5: + String var6 = var0[0]; + int var2 = Integer.parseInt(var0[1]); + String var3 = var0[2]; + int var4 = Integer.parseInt(var0[3]); + String var5 = var0[4]; + //Class var7 = ClassLoader.getSystemClassLoader().loadClass(var6); + Class var7 = GBNTReceiver.class; + Receiver var8 = (Receiver)var7.newInstance(); + var8.setErrorPercentage((double)var1); + var8.connect(var2, InetAddress.getByName(var3), var4); + byte[] var9 = new byte[1024]; + FileOutputStream var11 = new FileOutputStream(var5); + + int var10; + while((var10 = var8.receive(var9, 0, 1024)) > 0) { + var11.write(var9, 0, var10); + } + + var11.close(); + var11 = null; + var8.disconnect(); + return; + default: + System.err.println("Usage: FileReceiver [error%]"); + } + } +} diff --git a/hw2/src/transport/FileSender.java b/hw2/src/transport/FileSender.java new file mode 100644 index 0000000..f29de1e --- /dev/null +++ b/hw2/src/transport/FileSender.java @@ -0,0 +1,53 @@ +package transport; + +import java.io.File; +import java.io.FileInputStream; +import java.net.InetAddress; +import impl.GBNTSender; + +public class FileSender { + + public static void main(String[] var0) throws Exception { + int var1 = 0; + switch(var0.length) { + case 6: + var1 = Integer.parseInt(var0[5]); + case 5: + String var6 = var0[0]; + int var2 = Integer.parseInt(var0[1]); + String var3 = var0[2]; + int var4 = Integer.parseInt(var0[3]); + String var5 = var0[4]; + //Class var7 = ClassLoader.getSystemClassLoader().loadClass(var6); + Class var7 = GBNTSender.class; + Sender var8 = (Sender)var7.newInstance(); + var8.setErrorPercentage((double)var1); + var8.connect(var2, InetAddress.getByName(var3), var4); + File var9 = new File(var5); + + try { + FileInputStream var10 = new FileInputStream(var9); + byte[] var11 = new byte[4000]; + + int var12; + int var14; + while((var12 = var10.read(var11)) != -1) { + for(int var13 = 0; var12 > 0; var13 += var14) { + var14 = var8.send(var11, var13, var12); + var12 -= var14; + } + } + + var10.close(); + } catch (Exception var15) { + var15.printStackTrace(); + } + + var8.close(); + var8.disconnect(); + return; + default: + System.err.println("Usage: FileSender [error%]"); + } + } +} diff --git a/hw2/src/transport/MyTimer.java b/hw2/src/transport/MyTimer.java new file mode 100644 index 0000000..fbcdc58 --- /dev/null +++ b/hw2/src/transport/MyTimer.java @@ -0,0 +1,81 @@ +package transport; + +import java.util.Vector; + +class MyTimer implements Runnable { + Object monitor; + Vector actions; + boolean active; + + public MyTimer(Object var1) { + this.monitor = var1; + this.actions = new Vector(); + this.active = true; + Thread var2 = new Thread(this); + var2.setDaemon(true); + var2.start(); + } + + public synchronized void stop() { + this.active = false; + } + + public synchronized void schedule(TimeoutAction var1, long var2) { + long var4 = System.currentTimeMillis() + var2; + + int var6; + for(var6 = 0; var6 < this.actions.size() && ((ScheduledAction)this.actions.elementAt(var6)).time < var4; ++var6) { + } + + this.actions.insertElementAt(new ScheduledAction(var4, var1), var6); + this.notify(); + } + + synchronized TimeoutAction getFirst() throws InterruptedException { + while(true) { + if (this.active) { + if (this.actions.isEmpty()) { + this.wait(); + continue; + } + + long var1 = ((ScheduledAction)this.actions.elementAt(0)).time - System.currentTimeMillis(); + if (var1 > 0L) { + this.wait(var1); + continue; + } + + return ((ScheduledAction)this.actions.remove(0)).action; + } + + return null; + } + } + + public synchronized void cancel(TimeoutAction var1) { + for(int var2 = 0; var2 < this.actions.size(); ++var2) { + if (((ScheduledAction)this.actions.elementAt(var2)).action == var1) { + this.actions.remove(var2); + break; + } + } + + } + + public void run() { + try { + while(true) { + TimeoutAction var1 = this.getFirst(); + if (var1 == null) { + return; + } + + synchronized(this.monitor) { + var1.timeoutAction(); + } + } + } catch (Exception var5) { + System.err.println("MyTimer: " + var5); + } + } +} diff --git a/hw2/src/transport/Receiver.java b/hw2/src/transport/Receiver.java new file mode 100644 index 0000000..9e74f3b --- /dev/null +++ b/hw2/src/transport/Receiver.java @@ -0,0 +1,65 @@ +package transport; + +public abstract class Receiver extends Transport { + private boolean writing_allowed = true; + private byte[] rcv_buffer = new byte[1024]; + private int rcv_offset; + private int rcv_length; + + public Receiver() { + } + + public final synchronized int receive(byte[] var1, int var2, int var3) throws InterruptedException { + while(this.rcv_length == 0) { + this.wait(); + } + + if (this.rcv_length < 0) { + return this.rcv_length; + } else if (this.rcv_length > var3) { + for(int var4 = 0; var4 < var3; ++var4) { + var1[var2 + var4] = this.rcv_buffer[this.rcv_offset + var4]; + } + + this.rcv_offset += var3; + this.rcv_length -= var3; + this.notifyAll(); + return var3; + } else { + var3 = this.rcv_length; + + do { + --this.rcv_length; + var1[var2 + this.rcv_length] = this.rcv_buffer[this.rcv_offset + this.rcv_length]; + } while(this.rcv_length > 0); + + this.notify(); + return var3; + } + } + + protected final void deliver(byte[] bytes, int offset, int length) { + if (length == 0) { + System.err.println("Receiver: error: deliver was called with zero length."); + } else { + synchronized(this) { + try { + while(this.rcv_length != 0) { + this.wait(); + } + + for(int i = 0; i < length; ++i) { + this.rcv_buffer[i] = bytes[i + offset]; + } + + this.rcv_offset = 0; + this.rcv_length = length; + this.notifyAll(); + } catch (InterruptedException ignored) { + System.err.println("Receiver.deliver: warning: interrupted while delivering to application. Some data might be lost."); + } + + } + } + } +} diff --git a/hw2/src/transport/ScheduledAction.java b/hw2/src/transport/ScheduledAction.java new file mode 100644 index 0000000..f5afc15 --- /dev/null +++ b/hw2/src/transport/ScheduledAction.java @@ -0,0 +1,11 @@ +package transport; + +class ScheduledAction { + long time; + TimeoutAction action; + + public ScheduledAction(long var1, TimeoutAction var3) { + this.time = var1; + this.action = var3; + } +} diff --git a/hw2/src/transport/Sender.java b/hw2/src/transport/Sender.java new file mode 100644 index 0000000..2847fcc --- /dev/null +++ b/hw2/src/transport/Sender.java @@ -0,0 +1,34 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package transport; + +public abstract class Sender extends Transport { + private boolean sending_allowed = true; + + public Sender() { + } + + public final synchronized int send(byte[] var1, int var2, int var3) throws InterruptedException { + while(!this.sending_allowed) { + this.wait(); + } + + return this.reliableSend(var1, var2, var3); + } + + protected final synchronized void blockSender() { + this.sending_allowed = false; + } + + protected final synchronized void resumeSender() { + this.sending_allowed = true; + this.notifyAll(); + } + + protected abstract int reliableSend(byte[] var1, int var2, int var3); + + public abstract void close(); +} diff --git a/hw2/src/transport/TimeoutAction.java b/hw2/src/transport/TimeoutAction.java new file mode 100644 index 0000000..055f43e --- /dev/null +++ b/hw2/src/transport/TimeoutAction.java @@ -0,0 +1,5 @@ +package transport; + +public interface TimeoutAction { + void timeoutAction(); +} diff --git a/hw2/src/transport/Transport.java b/hw2/src/transport/Transport.java new file mode 100644 index 0000000..3fa7779 --- /dev/null +++ b/hw2/src/transport/Transport.java @@ -0,0 +1,118 @@ +package transport; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.util.Random; + +public abstract class Transport implements Runnable { + private DatagramSocket socket = null; + private DatagramPacket rcv_packet = null; + private DatagramPacket snd_packet = null; + private static Random generator = new Random(); + private double error = 0.0D; + private boolean disconnect_allowed = true; + private MyTimer timer = new MyTimer(this); + private Thread listener; + public static final int MAX_PACKET_SIZE = 1024; + public static final int END_OF_STREAM = -1; + + public Transport() { + } + + public final synchronized void connect(int var1, InetAddress var2, int var3) throws IllegalArgumentException, SocketException { + this.socket = new DatagramSocket(new InetSocketAddress(var1)); + this.socket.connect(var2, var3); + this.socket.setSoTimeout(60000); + if (this.snd_packet == null) { + this.snd_packet = new DatagramPacket(new byte[1024], 1024, var2, var3); + } else { + this.snd_packet.setAddress(var2); + this.snd_packet.setPort(var3); + } + + if (this.rcv_packet == null) { + this.rcv_packet = new DatagramPacket(new byte[1024], 1024); + } + + if (this.listener == null) { + this.listener = new Thread(this); + this.listener.setDaemon(true); + this.listener.start(); + } + + } + + protected final synchronized void allowDisconnect() { + this.disconnect_allowed = true; + this.notifyAll(); + } + + protected final synchronized void blockDisconnect() { + this.disconnect_allowed = false; + } + + public final synchronized void disconnect() throws InterruptedException { + while(!this.disconnect_allowed) { + this.wait(); + } + + if (this.socket != null) { + this.socket.close(); + this.listener.interrupt(); + this.socket = null; + this.listener = null; + this.timer.stop(); + } + } + + protected abstract void unreliableReceive(byte[] var1, int var2, int var3); + + protected final void unreliableSend(byte[] var1, int var2, int var3) { + if (generator.nextDouble() >= this.error) { + try { + this.snd_packet.setData(var1, var2, var3); + this.socket.send(this.snd_packet); + } catch (Exception var5) { + System.err.println("unreliableSend: error sending packet: " + var5); + var5.printStackTrace(); + } + } + } + + public final void setErrorPercentage(double var1) { + this.error = var1 / 100.0D; + } + + public final synchronized void setTimeout(long var1, TimeoutAction var3) { + this.timer.schedule(var3, var1); + } + + public final synchronized void cancelTimeout(TimeoutAction var1) { + this.timer.cancel(var1); + } + + public final void run() { + while(this.socket != null) { + try { + this.socket.receive(this.rcv_packet); + synchronized(this) { + this.unreliableReceive(this.rcv_packet.getData(), this.rcv_packet.getOffset(), this.rcv_packet.getLength()); + } + } catch (IOException var6) { + synchronized(this) { + if (this.socket == null) { + return; + } + } + + System.err.println("run: error receiving data from the network: " + var6); + System.err.println("Terminating."); + return; + } + } + } +} diff --git a/hw2/transport.jar b/hw2/transport.jar deleted file mode 100644 index f4b2dbc95f41274e165a35b35c16d340074931cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8903 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 XRRzR{s}BQ%{_s10xb3a~GU@*TIy;j`