diff --git a/lab8_protocols/CS380_lab8-part1.docx b/lab8_protocols/CS380_lab8-part1.docx new file mode 100644 index 0000000..3ecf272 Binary files /dev/null and b/lab8_protocols/CS380_lab8-part1.docx differ diff --git a/lab8_protocols/CS380_lab8-part1.pdf b/lab8_protocols/CS380_lab8-part1.pdf new file mode 100644 index 0000000..1afbabd Binary files /dev/null and b/lab8_protocols/CS380_lab8-part1.pdf differ diff --git a/lab8_protocols/CS380_lab8-part2.pdf b/lab8_protocols/CS380_lab8-part2.pdf new file mode 100644 index 0000000..84a0482 Binary files /dev/null and b/lab8_protocols/CS380_lab8-part2.pdf differ diff --git a/lab8_protocols/CS380_lab8.zip b/lab8_protocols/CS380_lab8.zip new file mode 100644 index 0000000..175b853 Binary files /dev/null and b/lab8_protocols/CS380_lab8.zip differ diff --git a/lab8_protocols/Lab Assignment - protocols.pdf b/lab8_protocols/Lab Assignment - protocols.pdf new file mode 100644 index 0000000..e130672 Binary files /dev/null and b/lab8_protocols/Lab Assignment - protocols.pdf differ diff --git a/lab8_protocols/jasper-1.7.zip b/lab8_protocols/jasper-1.7.zip new file mode 100644 index 0000000..bd948e7 Binary files /dev/null and b/lab8_protocols/jasper-1.7.zip differ diff --git a/lab8_protocols/jasper/README.txt b/lab8_protocols/jasper/README.txt new file mode 100755 index 0000000..128d4d8 --- /dev/null +++ b/lab8_protocols/jasper/README.txt @@ -0,0 +1,16 @@ +README.txt K. J. Turner 28th September 2015 + +This is version 1.7 of Jasper (Java Simulation of Protocols for Education and +Research): https://sourceforge.net/projects/jaspersimulator. + +Open "html/index.html" in a web browser to try out the protocol simulations. +(Due to Java security restrictions you must authorise running of code created by +the University of Stirling.) + +Open "docs/index.html" in a web browser for more detail and the licence +conditions. + +Note that simulations of CSMA/CD, Multicast, Multiplexing, Protocol Stack and +TCP Slow Start were created for Pearson Education to accompany the ninth edition +of "Data and Computer Communications" by William Stallings. + diff --git a/lab8_protocols/jasper/build.xml b/lab8_protocols/jasper/build.xml new file mode 100755 index 0000000..8e4185d --- /dev/null +++ b/lab8_protocols/jasper/build.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab8_protocols/jasper/docs/index.html b/lab8_protocols/jasper/docs/index.html new file mode 100755 index 0000000..9c9457d --- /dev/null +++ b/lab8_protocols/jasper/docs/index.html @@ -0,0 +1,534 @@ + + + + + + + + + Jasper Protocol Simulator (Java Simulation of Protocols for Education and Research) + + + + + + + +
+ +

+ Jasper Protocol Simulator +
+ (Java Simulation of Protocols for Education and Research) +

+ +
+ + + +

Description

+ +

+ This protocol simulator provides: +

+ + + +

+ The simulator is written in Java, and so can be used on many platforms with + a web browser (subject to applet security) and/or a Java environment. To + compile or extend the simulator requires a Java development environment such + as the Oracle JDK (and ideally also Apache Ant). To run simulations requires + a Java runtime environment such as the Oracle JRE. The code has been + pre-built using JDK 1.8. However, it should compile and run using Java 5 + onwards. +

+ +

+ (A Serbo-Croat + version of this page was produced by Anja Skrba.) +

+ + + +

Installation

+ +

+ The simulator is provided as a Zip archive. The simulator unpacks + to a directory called jasper-N.N according to version number. You + might rename this to jasper for simplicity. The distribution + contains: +

+ +
+ +
build.xml
+
Ant build file to manage the code
+ +
docs
+
basic documentation
+ +
html
+
directory for protocol simulation pages and simulator JAR archive
+ +
scenarios
+
example scenario files
+ +
source
+
directories for Java code (protocol, simulator, support)
+ +
+ + + +

Simulation as an Applet

+ +

+ The simulator can be used on various protocols by opening + html/index.html in a web browser. This assumes you renamed the + distribution folder as jasper. This will bring up the main page, + with general instructions and a list of protocols that can be simulated. + Your web browser will need to be configured to run Java applets and + (ideally) JavaScript. Due to Java security restrictions you must authorise + running of the pre-compiled code created by the University of Stirling. + Alternatively, use an applet viewer on the files (though the JavaScript to + set protocol parameters will not work). +

+ +

+ Most simulations are in three-column format: two protocol entities + (sender/receiver or A/B) and a communications medium (link or network). A + few simulations add the sending and receiving users (applications) in outer + columns. This format is useful for showing what the user sees; typically + this is only part of what is happening in the protocol. +

+ +

+ In a web browser, select an action by clicking on the list at the bottom + right of the diagram. Actions are things like a user sending a data message + or the medium delivering a protocol message. You are completely in control + of the simulation. For example you may decide when to send messages, whether + to acknowledge messages, and whether to deliver or lose messages in the + medium. Since the simulation does not run in real-time, a timeout is + possible as soon as a message has been sent. +

+ +

+ The last action in the diagram is shown in red. Various protocol comments + may also be noted in green the diagram, e.g. that a timeout occurred or that + a message was ignored. +

+ +

+ If you make a mistake, or just want to backtrack in the simulation, then + click Undo. You can undo as many steps as you like, right up to the + beginning of the simulation. Clicking on Redo will perform again + the last undone step. Clear will restart the simulation with the + current protocol parameters. If you click on Run the simulator will + run automatically, making random choices for you. If you are not sure what + to experiment with, this is an easy way of seeing the protocol in action. + While this is happening, the Run button changes to Stop. + Click on Stop to return the simulator to user control. You can + continue at this point as if you had made all the automatic choices + yourself. +

+ +

+ The Print, Load and Save buttons are disabled + since the simulations are running as applets. If you wish hard copy of the + simulation, position the scroll bar in the simulation pane at an interesting + point and print out the whole web page. +

+ +

+ Some simulations have associated protocol parameters. To change the defaults + that are shown, enter new values and click Change Values. In some + cases, this will force the simulation to restart. The following is a + screen-shot of a TCP client-server simulation, showing the protocol + parameters and the main simulation controls. +

+ + + + +
+ TCP Simulation +
+ + + +

Simulation as an Application

+ +

+ Running a protocol simulation as an application gives access to the + Print, Load and Save buttons. +

+ +

+ Print produces a hard copy of the whole simulation scenario. + Note that individual pages cannot be selected for printing. The width of the + printout is determined by the width of the on-screen window. Constants + winWidth and winHeight in + ProtocolSimulator define the initial window size. Constant + maxHeight in TimeSequenceDiagram defines the + vertical size of printed pages. The current sizes are appropriate for A4 + paper. If necessary, change them for (say) US letter. +

+ +

+ Load loads a simulation scenario file (with a name ending in + .scn); this must be for the same protocol as you are currently + simulating. It replaces the current simulation scenario (if any). + Save saves the current simulation as a scenario file (with a + name ending in .scn). If you are adventurous, you can create and + edit your own scenarios using a text editor. +

+ +

+ When the simulator is run as an application, a mandatory protocol name + follows the main simulator class. Protocol parameters may then be given if + required; protocols have defaults for these. Assume that the simulator has + been built and is to be started from the top level of Jasper. The following + (split here across two lines) will run TCP in client-server mode, with + message window sizes other than the default: +

+ +
+    java -cp html/ProtocolSimulator.jar simulator.ProtocolSimulator
+      TCP/cs windowSizeA=500 windowSizeB=300
+  
+ + + +

Development

+ +

+ The complete source of the simulator is provided. (Most files have Unix + end-of-line.) The code should preferably be rebuilt using the Ant + build.xml build file. ant -p will print help + information about build targets. + ant simulator will rebuild ProtocolSimulator.jar + in the html directory. ant clean will remove all + compiled class files and backup files, but preserving the JAR file. + ant spotless will remove even this. +

+ +

+ If you do not have Ant you will need to compile the Java source files + manually and create a JAR file from the result. +

+ +

+ To modify an existing protocol simulation or to write a new one will need a + knowledge of the simulation framework. See the article An Interactive Visual Protocol Simulator for details of + this and an extended example of how to develop a simulation. Once the + framework is understood, a simple simulation can be developed in a day; + complex protocols could take a week or two to develop. +

+ +

+ Suppose that you want to develop a new simulation of the protocol + EXP ('Example Protocol'). You would need to write + EXP.java to instantiate the various entities in the protocol. + For a simple protocol, you would then write EXPSender.java and + EXPReceiver.java to define the behaviour of a sending or + receiving protocol entity. More complex protocols could involve defining + separate entities to handle the service interface and the protocol entity. + It might also be necessary to define the formats of protocol messages and a + variation on the underlying Medium to match these. +

+ +

+ Due to increased Java security, applets need to be signed by a proper + certificate. This has been done for the pre-compiled code. If you need to + rebuild the code you will need your own keystore and digital certificate. + See the Ant build file for how to use these. +

+ +

+ Bear in mind that much of the development work was undertaken by + students, so the level of comments in the code is somewhat limited in + places. The ABP simulation is the simplest of the protocols and is a good + place to start. The TFTP simulation is the best commented and best + explained of the protocols. It illustrates nearly all the key points in + simulation development. +

+ + + +

Licence

+ +

+ This program is free software. You can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation - either version 3 of the License, or (at your option) + any later version. +

+ +

+ This program is distributed in the hope that it will be useful but + without any warranty, without even the implied warranty + of merchantability or fitness for a particular + purpose. See the GNU General Public License for more details. +

+ + + +

Acknowledgements

+ +

+ The protocol simulator was developed by Computing Science and Mathematics at the University of Stirling. + Iain A. Robin undertook most of the development for his Master's project + under Ken Turner's supervision. Paul Johnson and Kenneth A. Whyte + contributed to the development of some of the simulations for their Master's + projects. Dr. Peter J. B. King, Heriot-Watt University, provided helpful + ideas and suggestions. +

+ + + +

History

+ + + + + + + diff --git a/lab8_protocols/jasper/docs/tcp-simulation.gif b/lab8_protocols/jasper/docs/tcp-simulation.gif new file mode 100755 index 0000000..7dac7e2 Binary files /dev/null and b/lab8_protocols/jasper/docs/tcp-simulation.gif differ diff --git a/lab8_protocols/jasper/html/ABP.html b/lab8_protocols/jasper/html/ABP.html new file mode 100755 index 0000000..3929c2e --- /dev/null +++ b/lab8_protocols/jasper/html/ABP.html @@ -0,0 +1,161 @@ + + + + + + + + + Alternating Bit Protocol + + + + + + + + + +
+ +

Alternating Bit Protocol Simulator

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ ABP (Alternating Bit Protocol) is a connection-less protocol for + transferring messages in one direction between a pair of protocol + entities. It is a simple form of the Sliding Window + Protocol with a window size of 1. The message sequence numbers simply + alternate between 0 and 1. +

+ +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This will cause the simulation to restart. +

+ +

+ Optionally set the level of control that the simulator user needs over + the medium: +

+ +
+ +
Automatic:
+
+ Messages are delivered immediately without loss. This is suitable for + initial experimentation, but limits what can be explored. For example, + message are never lost and never have to be timed out and resent. +
+ +
Delivery:
+
+ Alternatively, message delivery may be controlled - though messages + will still never be lost. Choose this option to allow messages to be + delivered in different orders and to be resent. +
+ +
Delivery/Loss:
+
+ Finally, delivery and loss of messages may be completely controlled. + This is the most comprehensive option, but also the most complex one to + manage. +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + +
Medium Control: + Automatic + DeliveryDelivery/Loss
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with transmitting + and receiving protocol entities, and a communications medium that carries + messages. The transmitter simply sends messages numbered DATA(0) + or DATA(1); the content of messages is not identified. These are + acknowledged with ACK(1) or ACK(0) respectively. Note + that if a DATA message is received again due to re-transmission, + it is acknowledged but discarded. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/ABRA.html b/lab8_protocols/jasper/html/ABRA.html new file mode 100755 index 0000000..081d0d4 --- /dev/null +++ b/lab8_protocols/jasper/html/ABRA.html @@ -0,0 +1,107 @@ + + + + + + + + + Abracadabra Protocol + + + + + + + +
+ +

Abracadabra Protocol Simulator

+ + Simulator Logo +
+ +

Protocol Description

+ +

+ Abracadabra (Alternating Bit Protocol with Connection and Disconnection) + is a connection-oriented protocol that allows data to be sent in either + direction using the Alternating Bit Protocol. Data + transfer is preceded by connection and followed by disconnection. +

+ +

+ The initiating user requests a connection with ConReq. This is + sent as a CR protocol message. The other user receives + ConInd as an indication that a connection attempt has been + received. Normally it will respond positively to the connection attempt + with ConResp. This leads to a CC confirmation in the + protocol, and a ConConf confirmation back to the initiating + user. However the responding user may also reject a connection attempt + with a DisReq disconnection request. This leads to a DR + in the protocol and DisInd at the originator. If CR + connection messages are sent by both protocol entities simultaneously, + each acknowledges the other and the connection is set up. +

+ +

+ Once a connection has been made, either user sends data messages as + DatReq(DATA0) or DatReq(DATA1); the content of messages + is not explicitly identified. These requests lead to delivery at the + other user with DatInd(DATA0) or DatInd(DATA1). The + protocol carries data as DT(0) or DT(1) messages that + give only the sequence number, not the data. These are acknowledged with + AK(1) or AK(0) respectively. Note that if a DT + message is received again due to re-transmission, it is acknowledged but + discarded. Both users may be sending data at the same time. +

+ +

+ Disconnection is requested with DisReq. This causes a + DR message to be sent in the protocol, indicating disconnection + at the other user with DisInd. The remote protocol entity + confirms disconnection with DC. If DR disconnection + messages are sent by both protocol entities simultaneously, each sends + back a DC as confirmation. Since both users tried to disconnect + at the same time, each has issued DisReq and does not see + DisInd. +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with users A and B, + protocol entities A and B that support them, and a communications medium + that carries messages. Either user may open a connection, then send data, + and finally break the connection. There may be only one connection at a + time between a pair of users. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/BOOTP.html b/lab8_protocols/jasper/html/BOOTP.html new file mode 100755 index 0000000..49dd447 --- /dev/null +++ b/lab8_protocols/jasper/html/BOOTP.html @@ -0,0 +1,93 @@ + + + + + + + + + Boot Protocol + + + + + + + +
+ +

Boot Protocol Simulator

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ BOOTP (Boot Protocol) is a simple connection-less protocol, typically + used by a discless workstation to discover its Internet address and/or + the name of its bootstrap file. BOOTP operates over UDP (User Datagram Protocol). BOOTP simply discovers + the parameters needed for the bootstrap procedure. Typically, TFTP (Trivial File Transfer Protocol) is used to + download the bootstrap file itself. +

+ +

+ A boot client supplies a transaction identifier and its hardware address. + The client may optionally supply its network address; the server will + allocate a unique network address if required. The client may optionally + supply the name of the boot file; the server will supply the full path + name if this file is given, or will determine the boot file the client + needs. Addresses and the boot file are given symbolic names in the + simulation. +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with client and + server protocol entities, and a communications medium that carries + messages. The client sends a REQUEST message containing an + (arbitrary) transaction identifier and its hardware address + (hw). The client may optionally supply its network address if it + knows it (e.g. net.43 for some subnetwork net). The + client may additionally ask for the location of a particular bootstrap + file (boot). The server reply starts with the original + parameters. A network address is allocated to the client if it did not + know it. The full filestore path to a boot file is returned if the client + did not know which file to use. If the client named a boot file, the full + path to this is returned. If a REQUEST or REPLY message + is lost, the client must time out and send it again. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + + diff --git a/lab8_protocols/jasper/html/CSMA.html b/lab8_protocols/jasper/html/CSMA.html new file mode 100755 index 0000000..79854a7 --- /dev/null +++ b/lab8_protocols/jasper/html/CSMA.html @@ -0,0 +1,220 @@ + + + + + + + + + CSMA/CD ("Ethernet") + + + + + + + + + +
+ +

CSMA/CD (Ethernet)

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ CSMA/CD (Carrier Sense, Multiple Access, Collision Detection) illustrates + how multiple systems can share a common communications medium. CSMA/CD is + best known in the form of Ethernet - a kind of LANs (Local Area Network). + LANs have a MAC layer (Medium Access Control) that operates the data link + protocol. +

+ +

+ Many systems (known as stations) can be attached to an Ethernet - the + Multiple Access aspect. A station will not transmit if it finds that another + station is already using the Ethernet - the Carrier Sense aspect. However, + it is still possible for two stations to transmit at almost the same time. + While a station is transmitting, it may find that another station has + already begun its own transmission - the Collision Detection aspect. In such + a case, each station backs off and tries again - hopefully avoiding a + collision on the second or later attempt. +

+ +

+ An Ethernet is a broadcast medium: whatever one station sends is received by + all others. Each message carries the address of its intended destination; + only the station with this address will act on the message. A transmission + takes a very short (but not zero) time to arrive at its destination. Since + stations will be at different distances from each other, the transmission + time between them varies. The maximum transmission time between any two + stations is called the slot time. +

+ +

+ Ethernet does not provide any guarantee of correct delivery, so its protocol + can be simple. Instead, a higher-level protocol such as TCP (Transmission + Control Protocol) is responsible for detecting and correcting errors. +

+ +

+ Transmission proceeds in one of the following ways: +

+ +
+ +
Normal:
+
+ The station transmits its message from start to finish. When the message + has been fully received at the intended destination, that station + processes the message. +

+
+ +
Deferral:
+
+

+ In fact, a station listens for a transmission from another station + before beginning its own transmission. If it hears such a transmission, + it defers to the other station. Once that transmission has finished, the + station begins its own transmission. +

+
+ +
Collision:
+
+

+ However, this does not avoid interference. For example, a distant + station may have started transmission but its signal has not yet + arrived. The local station may therefore think the network is not in use + and begin its own transmission. Both transmissions will then collide in + the network. This can be detected by the transmitting stations, but this + is not always fully reliable. The stations therefore ensure that the + collision is properly detected by sending a jam signal. +

+ +

+ Following this, each station backs off a random delay (a number of slot + times) before retrying its transmission. Ideally, one station should + choose a smaller delay and so start retransmitting before the other one. +

+ +

+ For the first retransmission, the random delay is 0 or 1 slot times. + Since two stations may choose the same delay, there is a 50% chance that + they retry at the same point (and therefore collide again). On further + collisions, the maximum delay is doubled (0 to 2, 0 to 4, etc.). A limit + is set on the maximum number of retries. +

+
+ +
+ +

+ For this simulation, only two stations are involved; addresses are therefore + not required. For simplicity, the protocol simulation below assumes that the + LAN works perfectly (no message corruption, loss or misordering). However, + interference among transmissions is still a source of potential error that + the protocol must cope with. +

+ +

Protocol Parameters

+ +

+ By default, the simulation has a retry limit of 0. Since this means that + retries are not allowed, the simulation internally ensures that collisions + cannot occur. Retry limits from 1 to 5 can be used, but the possibility of + collisions complicates the behaviour. Click Change Settings after altering + the retry limit. This will restart the whole simulation. +

+ +
+ +
+ + + + + + + + + + + + +
Retry Limit (0 = no collisions, 5 maximum):
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with two stations, the + MAC entities that support them, and a LAN that carries messages between + them. Stations transmit messages in the format DATA(Dn). Data + messages are numbered D0, D1, etc.; no explicit data + content is given. If the retry limit is reached, this is reported to a + station with the message FAIL(Retry Limit). Protocol messages + have the form START(Dn) to start a transmission, + FINISH(Dn) to finish the transmission, and JAM to jam + transmission. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/HTTP.html b/lab8_protocols/jasper/html/HTTP.html new file mode 100755 index 0000000..6831702 --- /dev/null +++ b/lab8_protocols/jasper/html/HTTP.html @@ -0,0 +1,102 @@ + + + + + + + + + HyperText Transfer Protocol + + + + + + +
+ +

HyperText Transfer Protocol Simulator +

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ HTTP (Hypertext Transfer Protocol) is familiar as the mechanism for + delivering web pages. HTTP operates over TCP + (Transmission Control Protocol) in client-server mode for reliable + transfer of data. URLs (Uniform Resource Locators) and information + contents are represented symbolically in the simulation (URLn, + DATAn). The protocol simulation deals with the main commands: +

+ + + +

+ The simulation supports a limited range of response codes: +

+ + + +

+ The protocol simulation shows a time-sequence diagram with client and + server protocol entities, and a communications medium that carries + messages. The client sends a one of the above commands and a URL. The + server makes one of the above replies. Since the medium is assumed to be + reliable, messages are never lost (though they may be delayed). +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + + diff --git a/lab8_protocols/jasper/html/IP.html b/lab8_protocols/jasper/html/IP.html new file mode 100755 index 0000000..83b49a8 --- /dev/null +++ b/lab8_protocols/jasper/html/IP.html @@ -0,0 +1,199 @@ + + + + + + + + + + Internet Protocol + + + + + + + + + + +
+ +

Internet Protocol Simulator

+ + Simulator Logo + +
+ +

+ IP (Internet Protocol) a simple connection-less protocol for transferring + datagrams in either direction between a pair of hosts. +

+ +

+ The protocol simulation shows a time-sequence diagram with users A and B, + protocol entities A and B that support them, and a communications medium + that carries messages. Users request data transmissions with + DatReq(DATAn), and receive data transmissions + as DatInd(DATAn). Protocol messages are sent as + DT(MID,Offset,Length) that gives the message identifier + n, its byte offset relative to the whole user message, and the + length of the message (fragment). The user message size may exceed the + maximum protocol message size, which leads to fragmentation. The maximum + protocol message size may also exceed that for the medium, in which case + there may be further fragmentation. Each fragment is followed by `+' + to mean that it is not the last; the very last fragment is followed by + `-'. (This is equivalent to the More Fragments flag in the + protocol.) +

+ +

+ You may choose if the receiving protocol entity should time out a partly + received message. Since the simulation does not run in real-time, a + message's Time-To-Live (TTL) may expire as soon as it has been + received in part. There is no control over fragmentation in the + simulation; this is handled automatically based on the protocol + parameters. Messages may also be randomly lost or misordered, based on + the parameter settings. +

+ +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
User Message Size (10 to 1000): + +
Maximum Protocol Message Size (10 to 1000): + +
Maximum Medium Message Size (10 to 1000): + +
Loss Rate (0.0 = lose none, 1.0 = lose all):
Medium Message Misordering + +
+ +
+ +
+ +
+ +

Protocol Simulation

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/MCAST.html b/lab8_protocols/jasper/html/MCAST.html new file mode 100755 index 0000000..46c5dac --- /dev/null +++ b/lab8_protocols/jasper/html/MCAST.html @@ -0,0 +1,182 @@ + + + + + + + + + Multicasting + + + + + + + + + +
+ +

Multicasting

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ MCAST (Multicasting) illustrates how data can be sent from a source to + multiple destinations over a network. The following example shows a source, + three destinations, and a further host that has no interest in the data. + These are interconnected by routers A to F. +

+ +
+ + Multicast Illustration + +
+ +

+ There are three strategies for sending data from the source to the + destinations: +

+ +
+ +
Broadcast:
+
+

+ If the source does not know which networks the destinations are on, it + must send a copy of the messages to each router that gives access to a + possible destination. In this example, the source must send data to + routers B, D, E and F. The result is inefficient in that 10 copies of + the message must be sent. +

+
+ +
Unicast:
+
+

+ If the source knows which networks the destinations are on, it can send + a copy of the messages only to the routers that gives access to these + destinations. In this example, the source must send data to routers B, D + and E. The result is more efficient than broadcast, but 8 copies of the + message must still be sent. +

+
+ +
Multicast:
+
+

+ The source can instead rely on the routers to distribute messages, + making copies only when necessary. In this example, the source sends + just one message to router A, stating that it is for routers B, D and E. + Router A looks at where the message is going, and delivers copies of the + message to routers B and C (when it states that the message is for + routers D and E). Router C then delivers copies of the message to + routers D and E. The result is more efficient than unicast, with only 5 + copies of messages being sent. This is half the number for broadcast. On + a much larger network, the transmission savings of multicast can be + substantial. +

+
+ +
+ +

+ Each router stores the messages it receives and forwards them to their + destination. The messages may be forwarded in any order since it depends on + how busy the communications links are. When trying the simulation, you may + find it easier to understand if you immediately forward router messages + rather than let them build up. For this simulation, the communications + channels are assumed to operate perfectly (no message corruption, loss or + misordering). +

+ +

Protocol Parameters

+ +

+ By default, multicasting uses the broadcast strategy. You can modify the way + in which the protocol works by modifying the strategy. Click Change Settings + after making this alteration. This will restart the whole simulation. +

+ +
+ +
+ + + + + + + + + + + + +
Multicasting Strategy: + +
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with the source and + the six routers. Messages have the format + DT(Destinations,Mn). For broadcast and unicast, each + message has one destination; for multicast, there can be multiple + destinations. Messages are numbered M0, M1, etc.; no + explicit data + content is given. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/MUX.html b/lab8_protocols/jasper/html/MUX.html new file mode 100755 index 0000000..1b5d0c3 --- /dev/null +++ b/lab8_protocols/jasper/html/MUX.html @@ -0,0 +1,190 @@ + + + + + + + + + Multiplexer + + + + + + + + + +
+ +

Multiplexer

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ MUX (Multiplexer) illustrates how data can be multiplexed among multiple + sources and sinks via a shared communications channel. Each source + independently sends data indexed by its number to the corresponding sink + with the same number. At the source end, a multiplexer combines data from + the sources and sends it over a shared channel. At the sink end, a + demultiplexer splits the incoming channel data and sends it to the + corresponding sinks. +

+ +
+ + Multiplexer Illustration + +
+ +

+ For this simulation, the communications channel is assumed to operate + perfectly (no message corruption, loss or misordering). When the multiplexer + sends a message, it therefore arrives directlyat the demultiplexer. It + follows that acknowledgements, sequence number and timeouts are not + applicable. +

+ +

+ The multiplexer and demultiplexer exchange messages in a format that depends + on the kind of multiplexing: +

+ + + +

+ Perfect multiplexing would always operate faster than sources supply data + and sinks consume it. As an option, the simulation can allow multiplexing to + be slower than this. The result is that new data can overwrite data that has + been previously stored but not yet delivered. +

+ +

Protocol Parameters

+ +

+ By default, multiplexing uses 2 sources/sinks, is synchronous, and does not + overwrite previous data. You can modify the way in which the protocol works + by modifying these settings (e.g. 1 to 5 sources/sinks). Click Change + Settings after making these alterations. This will restart the whole + simulation. +

+ +
+ +
+ + + + + + + + + + + + + + + + +
Number of Sources/Sinks:Asynchronous Multiplexing:Data Overwriting:
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with sources on one + side and sinks on the other, the multiplexer and demultiplexer that support + them, and a communications channel that carries messages. Sources request + data transmission with + DATA(Source,Dn); the same messages arrive at sinks. + Sources/sinks are numbered 0, 1, etc. Data + messages are numbered D0, D1, etc.; no explicit data + content is given. The source/sink number and data number are usually + different (e.g. source 1 might sent data D7). +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/ProtocolSimulator.jar b/lab8_protocols/jasper/html/ProtocolSimulator.jar new file mode 100755 index 0000000..5297141 Binary files /dev/null and b/lab8_protocols/jasper/html/ProtocolSimulator.jar differ diff --git a/lab8_protocols/jasper/html/SMTP.html b/lab8_protocols/jasper/html/SMTP.html new file mode 100755 index 0000000..c43cbbc --- /dev/null +++ b/lab8_protocols/jasper/html/SMTP.html @@ -0,0 +1,133 @@ + + + + + + + + + Simple Mail Transfer Protocol + + + + + + + +
+ +

Simple Mail Transfer Protocol Simulator

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ SMTP (Simple Mail Transfer Protocol) is used to transfer email messages. + SMTP operates over TCP (Transmission Control + Protocol) in client-server mode for reliable transfer of data. The + protocol simulation deals with the main commands (sent in approximately + the order below): +

+ + + +

+ The simulation supports a limited range of response codes: +

+ + + +

+ After the client connects to the server using TCP, the server reports its + readiness with a 220 Server ready response. The client names + itself in HELO, to which the server normally gives a 250 + Server hello to client response. To send mail, the client issues + MAIL FROM and normally gets a 250 Sender OK response. + Recipients are named in RCPT TO, normally obtaining 250 + Recipient OK responses. However the server can reject a sender or + recipient with a 550 Sender invalid or 550 Recipient + invalid response. Once all parties have been named, the client sends + DATA to begin message transmission; a 354 Send mail + response is expected from the server. At this point, the real protocol + would send the lines of the message followed by a full stop. In the + simulation, a single Mail Message command stands for this. The + server will normally give a 250 Message accepted response and + further messages can be sent. Finally, the client sends QUIT and + the server responds with a 221 Server closing code. At this + point the TCP connection is broken. +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/STACK.html b/lab8_protocols/jasper/html/STACK.html new file mode 100755 index 0000000..c5a6224 --- /dev/null +++ b/lab8_protocols/jasper/html/STACK.html @@ -0,0 +1,101 @@ + + + + + + + + + Protocol Stack + + + + + + + +
+ +

Protocol Stack

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ STACK (Protocol Stack) illustrates how data flows through a typical protocol + stack. As an application message passes down through the layers towards the + medium, each layer prefixes the message with its own control header. + Eventually the message with all headers is sent via the medium to its + destination. In the reverse direction, as a medium message passes up through + the layers towards the application, each layer interprets and strips off its + control header. Eventually the message with no headers is passed to the + application. +

+ +

+ The Data Link layer is an exception because it usually adds a trailer + (typically a checksum) as well as its header. This trailer and header are + stripped off on reception.. +

+ +

+ For this simulation, the communications channel is assumed to operate + perfectly (no message corruption, loss or misordering). There is also no + fragmentation (layers do not split messages up) and no blocking (layers do + not combine messages). It follows that each application message corresponds + to one message over the medium. +

+ +

+ The elements of a message are simply numbered 0, 1, + etc. withou explicit data content. An Application sends a message with data + such as A3. This is then prefixed with a Transport header + T3, a Network header N3, a Link header and trailer + L3, and a Physical header P3. When sent over the + medium, the whole message then looks like P3:L3:N3:T3:A3:L3. On + reception, the headers (and Link trailer) are stripped off so that the + receiving Application gets A3 as sent. +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with the following + layers: Application (e.g. File Transfer Protocol), Transport (e.g. + Transmission Control Protocol), Network (e.g. Internet Protocol), Data Link + (e.g. Ethernet), Physical (e.g. Ethernet) and Medium (e.g. Unshielded + Twisted Pair cable). +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/SWP3.html b/lab8_protocols/jasper/html/SWP3.html new file mode 100755 index 0000000..0b30d67 --- /dev/null +++ b/lab8_protocols/jasper/html/SWP3.html @@ -0,0 +1,234 @@ + + + + + + + + + Sliding Window Protocol (3-Column) + + + + + + + + + +
+ +

+ Sliding Window Protocol Simulator +
+ (3-Column) +

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ SWP (Sliding Window Protocol) a connection-less protocol. It allows data + to be sent in one direction between a pair of protocol entities, subject to + a maximum number of unacknowledged messages. If SWP is operated with a + window size of 1, it is equivalent to the Alternating Bit + Protocol. The simulation below does not include the users; see the five-column version for this. +

+ +

+ The protocol has a maximum number of messages that can be sent without + acknowledgement. If this window becomes full, the protocol is blocked + until an acknowledgement is received for the earliest outstanding + message. At this point the transmitter is clear to send more messages. +

+ +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This may cause the simulation to restart. +

+ +

+ Optionally set the level of control that the simulator user needs over + the medium: +

+ +
+ +
Automatic:
+
+ Messages are delivered immediately without loss. This is suitable for + initial experimentation, but limits what can be explored. For example, + message are never lost and never have to be timed out and resent. +
+ +
Delivery:
+
+ Alternatively, message delivery may be controlled - though messages + will still never be lost. Choose this option to allow messages to be + delivered in different orders and to be resent. +
+ +
Delivery/Loss:
+
+ Finally, delivery and loss of messages may be completely controlled. + This is the most comprehensive option, but also the most complex one to + manage. +
+ +
+ +

+ Optionally set the maximum sequence number used by the protocol: this is + one less than the modulus. Sequence numbers wrap round to zero when they + pass the maximum. Optionally set the maximum window size permitted: this + must not exceed the maximum sequence number. Changing either of these + parameters will restart the simulation. +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Medium Control: + Automatic + + Delivery + + Delivery/Loss +
Maximum Sequence Number (1 to 31): + +
Window Size (1 to Maximum Sequence Number): + +
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with transmitting + and receiving protocol entities, and a communications medium that carries + messages. The transmitter simply sends messages numbered DT(0), + DT(1), etc. Once sequence numbers reach a maximum number (like + 7), they wrap back round to 0. The content of messages is not explicitly + identified. An acknowledgement AK(n) means that the DT + message numbered n is the next one expected (i.e. all messages + up to but not including this number have been received). Since sequence + numbers wrap round, an acknowledgement with sequence number 1 refers to + messages 0, 1, 7, 6, etc. Note that if a DT message is received + again due to re-transmission, it is acknowledged but discarded. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + + diff --git a/lab8_protocols/jasper/html/SWP5.html b/lab8_protocols/jasper/html/SWP5.html new file mode 100755 index 0000000..bcc5823 --- /dev/null +++ b/lab8_protocols/jasper/html/SWP5.html @@ -0,0 +1,169 @@ + + + + + + + + + + Sliding Window Protocol (5-Column) + + + + + + + + + + +
+ +

+ Sliding Window Protocol Simulator +
+ (5-Column) +

+ + Simulator Logo + +
+ +

+ SWP (Sliding Window Protocol) is a connection-less protocol in one + direction between a pair of users. It allows data to be sent in one + direction subject to a maximum number of unacknowledged messages. If SWP is + operated with a window size of 1, it is equivalent to the Alternating Bit Protocol. The simulation below includes + the users; the three-column version omits them. +

+ +

+ The protocol has a maximum number of messages that can be sent without + acknowledgement. If this window becomes full, the protocol is blocked + until an acknowledgement is received for the earliest outstanding + message. At this point the transmitter is clear to send more messages. +

+ +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This may cause the simulation to restart. +

+ +
+ +
+ + + + + + + + + + + + + + + + + +
Maximum Sequence Number (1 to 31):
Window Size (1 to Maximum Sequence Number):
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with users A and B, + protocol entities A and B that support them, and a communications medium + that carries messages. Users request data transmissions with + DatReq(DATAn), and receive data transmissions as + DatInd(DATAn). Data messages are simply numbered DATA0, + DATA1, etc. without explicit content. The transmitting protocol + sends the protocol message DT(n) that gives only the sequence + number, not the data. Once sequence numbers reach a maximum number (like + 7), they wrap back round to 0. An acknowledgement AK(n) means + that the DT message numbered n is the next one expected + (i.e. all messages up to but not including this number have been + received). Since sequence numbers wrap round, an acknowledgement with + sequence number 1 refers to messages 0, 1, 7, 6, etc. Note that if a + DT message is received again due to re-transmission, it is + acknowledged but discarded. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + + diff --git a/lab8_protocols/jasper/html/TCPcs.html b/lab8_protocols/jasper/html/TCPcs.html new file mode 100755 index 0000000..10e1093 --- /dev/null +++ b/lab8_protocols/jasper/html/TCPcs.html @@ -0,0 +1,280 @@ + + + + + + + + + Transmission Control Protocol (Client-Server) + + + + + + + + + +
+ +

+ Transmission Control Protocol Simulator +
+ (Client-Server) +

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ TCP (Transmission Control Protocol) is a connection-oriented protocol for + transferring data reliably in either direction between a pair of users. TCP + is a rather complex protocol, so it is easy to lose track of the simulation. + Try not to do anything too complicated! There is a peer-peer version as an alternative to this + client-server simulation. The slow start simulation + deals with only congestion avoidance. +

+ +

+ Users simply send messages of a fixed size; the content of messages is not + identified. The medium maximum packet size is the protocol segment size. + Depending on this, messages may be sent as a number of fragments. Data + transfer is also subject to the current window size of the receiver, and may + be held up if the receiver's window becomes full. +

+ +

+ To open a connection, a message is sent with the SYN (synchronise) flag. + To close a connection, a message is sent with the FIN (finish) flag. + Urgent messages may also be sent by selecting the PSH (push) flag as a + protocol parameter. +

+ +

+ When data arrives, it is not immediately delivered to the receiving user + unless the PSH flag is set. Ordinary data is accumulated, and can be + delivered later ("Deliver octets to user"). If the destination's + receiving window becomes full, new requests to send data will be buffered. + When the receiving window opens again, this buffered data can be sent + ("Send octets to peer"). +

+ +

+ Messages may contain a send sequence number (the offset of where the message + starts in the user's octet stream), an acknowledgement sequence number + (the offset of the next octet expected), and the current window (how many + octets can be received). +

+ +

+ TCP is rather complex, so the simulation does not attempt to faithfully + reflect all its details. Although the main paths should work as expected, it + may be possible to get the simulation into unusual states in which it does + not behave correctly. +

+ +

+ Things the simulation does not cover include the following. See advanced + guides to TCP for more information. +

+ + + +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This may cause the simulation to restart. +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
User A Message Size (10 to 1000): + + User A Push Flag:
User B Message Size (10 to 1000): User B Push Flag:
Protocol A Receive Window (10 to 1000):  
Protocol B Receive Window (10 to 1000):  
Medium/Protocol Segment Size (10 to 1000): + +
Loss Rate (0.0 = lose none, 1.0 = lose any):
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with a client and a + server, protocol entities that support them, and a communications medium + (network) that carries messages. The server initially passively opens + (waits) for a connection. The client may actively open a connection to the + waiting server. After a connection has been made, the client and the server + may send data messages to each other. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/TCPpp.html b/lab8_protocols/jasper/html/TCPpp.html new file mode 100755 index 0000000..b6bc4ad --- /dev/null +++ b/lab8_protocols/jasper/html/TCPpp.html @@ -0,0 +1,279 @@ + + + + + + + + + Transmission Control Protocol (Peer-Peer) + + + + + + + + + +
+ +

+ Transmission Control Protocol Simulator +
+ (Peer-Peer) +

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ TCP (Transmission Control Protocol) is a connection-oriented protocol for + transferring data reliably in either direction between a pair of users. TCP + is a rather complex protocol, so it is easy to lose track of the simulation. + Try not to do anything too complicated! There is a client-server version as an alternative to this + peer-peer simulation. The slow start simulation + deals with only congestion avoidance. +

+ +

+ Users simply send messages of a fixed size; the content of messages is not + identified. The medium maximum packet size is the protocol segment size. + Depending on this, messages may be sent as a number of fragments. Data + transfer is also subject to the current window size of the receiver, and may + be held up if the receiver's window becomes full. +

+ +

+ To open a connection, a message is sent with the SYN (synchronise) flag. + To close a connection, a message is sent with the FIN (finish) flag. + Urgent messages may also be sent by selecting the PSH (push) flag as a + protocol parameter. +

+ +

+ When data arrives, it is not immediately delivered to the receiving user + unless the PSH flag is set. Ordinary data is accumulated, and can be + delivered later ("Deliver octets to user"). If the destination's + receiving window becomes full, new requests to send data will be buffered. + When the receiving window opens again, this buffered data can be sent + ("Send octets to peer"). +

+ +

+ Messages may contain a send sequence number (the offset of where the message + starts in the user's octet stream), an acknowledgement sequence number + (the offset of the next octet expected), and the current window (how many + octets can be received). +

+ +

+ TCP is rather complex, so the simulation does not attempt to faithfully + reflect all its details. Although the main paths should work as expected, it + may be possible to get the simulation into unusual states in which it does + not behave correctly. +

+ +

+ Things the simulation does not cover include the following. See advanced + guides to TCP for more information. +

+ + + +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This may cause the simulation to restart. +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
User A Message Size (10 to 1000): + + User A Push Flag:
User B Message Size (10 to 1000): User B Push Flag:
Protocol A Receive Window (10 to 1000):  
Protocol B Receive Window (10 to 1000):  
Medium/Protocol Segment Size (10 to 1000): + +
Loss Rate (0.0 = lose none, 1.0 = lose any):
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with two peer service + users, protocol entities that support them, and a communications medium + (network) that carries messages. Either peer actively opens a connection, to + which the other peer responds. After a connection has been made, the two + peers may send data messages to each other. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/TCPss.html b/lab8_protocols/jasper/html/TCPss.html new file mode 100755 index 0000000..525c6ac --- /dev/null +++ b/lab8_protocols/jasper/html/TCPss.html @@ -0,0 +1,330 @@ + + + + + + + + + Transmission Control Protocol (Slow Start) + + + + + + + + + +
+ +

+ Transmission Control Protocol Simulator +
+ (Slow Start) +

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ TCP (Transmission Control Protocol) is a connection-oriented protocol for + transferring data reliably in either direction between a pair of users. TCP + is a rather complex protocol, so it is easy to lose track of the simulation. + Try not to do anything too complicated! There are client-server and peer-peer + simulations as alternatives that illustrate opening and closing connections. +

+ +

+ This simulation focuses on dynamic window management - specifically what is + called "slow start". This is designed to avoid the protocol + flooding the network with packets when it starts, and to ensure that the + protocol recovers slowly following message loss and timeout due to + congestion. +

+ +

+ Users simply send messages of a fixed size; the content of messages is not + identified. The medium maximum packet size is the protocol segment size. + Depending on this, messages may be sent as a number of fragments. Data + transfer is also subject to the current window size of the receiver, and may + be held up if the receiver's window becomes full. +

+ +

+ When data arrives, it is not immediately delivered to the receiving user. + Instead, data is accumulated and can be delivered later ("Deliver + octets to user"). If the receiving window becomes full, new requests to + send data will be buffered. When the receiving window opens again, this + buffered data can be sent ("Send octets to peer"). +

+ +

+ Messages may contain a send sequence number (the offset of where the message + starts in the user's octet stream), an acknowledgement sequence number + (the offset of the next octet expected), and the current window (how many + octets can be received). +

+ +

+ Slow start is governed by two protocol variables: cwind + (congestion window) and ssthresh (slow start threshold). These + are measured in octets for this simulation, but are sometimes counted as + numbers of segments. (Multiply the number of segments by the segment size to + get the number of octets.) The slow start procedure operates as follows: +

+ +
+ +
Initial Phase
+
+ The minimum of the congestion window size and the receiver window size + determines how much data can be sent. The protocol starts conservatively + with cwind set to the segment size. As each acknowledgement + arrives, cwind is increased by the segment size. The amount of + data that can be sent thus typically doubles in each round-trip time (i.e. + the time to send a message and get a response). The amount is ultimately + limited by the receiver's window. The available window thus increases + exponentially (shown as "exp." in the simulation when + cwind is increased). +
+ +
Congestion Action
+
+ When a timeout occurs and the protocol has to retransmit, the congestion + window is set back to one segment size. At this point, the threshold + ssthresh is set to half the previous value of + cwind. Suppose that the segment size is 200, so that + cwind starts at 200. On subsequent acknowledgements, + cwind could rise to 1600. If congestion causes loss at this + point, cwind will be set back to 200 and ssthresh + will be set to 800 (half of 1600). +
+ +
Congestion Recovery
+
+ Now the window is built up exponentially again. Once it reaches + ssthresh, it is no longer increased so aggressively. Instead, + cwind increases by one segment size for each round-trip time. + This is irrespective of how many acknowledgements arrive during this + period, unlike the exponential phase where every acknowledgement causes + the window to double per round trip time. During the recovery phase, the + available window increases linearly (shown as "lin." in the + simulation). The window will then become 900, 1000, etc. up to the + receiver maximum. If congestion again causes loss, the procedure is + repeated. +
+ +
+ +

+ Because the simulation does not run in real time, it does not reflect + round-trip times. As a result, its behaviour during the exponential and + linear phases appears similar. However, the phase is indicated depending on + whether cwind is below or above the threshold. +

+ +

+ TCP is rather complex, so the simulation does not attempt to faithfully + reflect all its details. Although the main paths should work as expected, it + may be possible to get the simulation into unusual states in which it does + not behave correctly. +

+ +

+ Things the simulation does not cover include the following. See advanced + guides to TCP for more information. +

+ + + +

Protocol Parameters

+ +

+ The following settings are adequate for a simple simulation. For a more + advanced exploration, choose different options and click Change + Settings. This may cause the simulation to restart. +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
User A Message Size (10 to 1000): + +
User B Message Size (10 to 1000):
Protocol A Receive Window (10 to 1000):
Protocol B Receive Window (10 to 1000):
Medium/Protocol Segment Size (10 to 1000): + +
Loss Rate (0.0 = lose none, 1.0 = lose any):
+ +
+ +
+ +
+ + + +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with two peer service + users, protocol entities that support them, and a communications medium + (network) that carries messages. The connection phase is not shown in this + simulation as it is assumed to have just occurred. Equally, the + disconnection phase is not shown. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/TFTP.html b/lab8_protocols/jasper/html/TFTP.html new file mode 100755 index 0000000..95bee4f --- /dev/null +++ b/lab8_protocols/jasper/html/TFTP.html @@ -0,0 +1,133 @@ + + + + + + + + + Trivial File Transfer Protocol + + + + + + + +
+ +

Trivial File Transfer Protocol Simulator

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ TFTP (Trivial File Transfer Protocol) is an elementary file transfer + protocol that is typically used by a discless workstation to download its + bootstrap file. TFTP operates over UDP (User Datagram + Protocol) and often in conjunction with BOOTP + (Boot Protocol). +

+ +

+ A client may initially issue: +

+ + + +

+ During data transfer, the client or server as appropriate sends: +

+ + + +

+ DATA blocks are numbered from 1 onwards with just a name + Dn for their data contents. Normally an ACK quotes this + number, but in the case of WRQ an acknowledgement with number 0 + is sent. All DATA blocks except the last one are full. The final + block is less than the normal size, shown with contents Dlast. +

+ +

+ The protocol is unusual in that both sender and receiver may time out. If + one party does not receive a fresh DATA block after sending an + ACK, it may retransmit the ACK in case it was lost. A + complication is that the ACK for the last DATA may be + lost. For this reason, the receiver must wait until it is satisfied that + data transfer is complete. If decision "presume all transmissions + over" is made prematurely, the sender will become stuck because it + cannot obtain the last ACK it needs. +

+ +

+ After completing transfer of a file, the client may start a new transfer. + ERROR messages may be sent in situations such as incorrect + sequence numbers or local disc errors. The full protocol allows for + different modes of transfer, but the simulation supports only octet mode + (the norm). +

+ +

Protocol Parameters

+ +

+ This simulation has no parameters. +

+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with client and + server protocol entities, and a communications medium that carries + messages. The client and server exchange the messages described above, + starting with an RRQ or WRQ from the client. Several + files may be transferred consecutively. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/UDP.html b/lab8_protocols/jasper/html/UDP.html new file mode 100755 index 0000000..fbb8a30 --- /dev/null +++ b/lab8_protocols/jasper/html/UDP.html @@ -0,0 +1,139 @@ + + + + + + + + + User Datagram Protocol + + + + + + + + + +
+ +

User Datagram Protocol Simulator

+ + Simulator Logo + +
+ +

Protocol Description

+ +

+ UDP (User Datagram Protocol) is a simple connection-less protocol for + transferring datagrams in either direction between a pair of user ports. + Each datagram is identified by its source and destination port numbers + (in the range 0 to 65535). +

+ +

Protocol Parameters

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
User A Source PortUser A Destination Port
User B Source PortUser B Destination Port
+ +
+ +
+ +
+ +

Protocol Simulation

+ +

+ The protocol simulation shows a time-sequence diagram with users A and B, + protocol entities A and B that support them, and a communications medium + that carries messages. Users request data transmissions with + DatReq(Src,Dst,Dn) and receive data transmissions as + DatInd(Src, Dst,Dn). Data messages are simply numbered D0, + D1, etc. in sequence for each user; no explicit data content is + given. Each user request leads to the protocol message + DT(Src,Dst,Dn) that gives the source port, destination port and + data message number. +

+ +
+ + + + + +
+ +
+ +

+ Up Arrow + Up one level to Protocol Simulators +

+ + + + + diff --git a/lab8_protocols/jasper/html/dot-orange.gif b/lab8_protocols/jasper/html/dot-orange.gif new file mode 100755 index 0000000..2210870 Binary files /dev/null and b/lab8_protocols/jasper/html/dot-orange.gif differ diff --git a/lab8_protocols/jasper/html/index.html b/lab8_protocols/jasper/html/index.html new file mode 100755 index 0000000..40d6091 --- /dev/null +++ b/lab8_protocols/jasper/html/index.html @@ -0,0 +1,188 @@ + + + + + + + + + Protocol Simulators + + + + + + + +
+ +

Protocol Simulators

+ + Simulator Logo + +
+ +

Available Simulations

+ +

+ The following protocol simulations were developed mainly by Iain Robin and + Ken Turner, + with contributions from Paul Johnson and Kenneth Whyte. They should be + useful in allowing you to experiment with a variety of protocols. Bug + reports should be sent to Ken Turner. +

+ +

+ When you run a simulation, due to Java security restrictions you will need + to authorise running the code (assuming the simulator JAR file is properly + signed). +

+ +

+ The simulations are listed in increasing order of complexity. It is + suggested that you try the easier protocols first. +

+ +
+ + Orange Dot + Alternating Bit Protocol (ABP) Simulator + +
Orange Dot + User Datagram Protocol (UDP) Simulator + +
Orange Dot + Boot Protocol (BOOTP) Simulator + +
Orange Dot + HyperText Transfer Protocol (HTTP) Simulator + +
Orange Dot + Simple Mail Transfer Transfer Protocol (SMTP) + Simulator + +
Orange Dot + Internet Protocol (IP) Simulator + +
Orange Dot + Sliding Window Protocol (SWP) 3-Column Simulator + +
Orange Dot + Sliding Window Protocol (SWP) 5-Column Simulator + +
Orange Dot + Abracadabra Protocol Simulator + +
Orange Dot + Trivial File Transfer Protocol (TFTP) Simulator + +
Orange Dot + Transmission Control Protocol (TCP) Client-Server + Simulator + +
Orange Dot + Transmission Control Protocol (TCP) Peer-Peer + Simulator + +
+ + + +

General Instructions

+ +

+ Select an action by clicking on the list at the bottom of the diagram. + Actions are things like a user sending a data message or the medium + delivering a protocol message. You are completely in control of the + simulation. For example you may have to decide when to send messages, + whether to acknowledge messages, and whether messages are lost in the medium + or are delivered. Since the simulation does not run in real-time, a timeout + is possible as soon as a message has been sent. +

+ +

+ The last action in the diagram is shown in red. If you make a mistake, or + just want to backtrack in the simulation, then click Undo. You can + undo as many steps as you like, right up to the beginning of the simulation. + Clicking on Redo will perform the last undone step again. + Clear will restart the simulation with the current protocol + parameters. If you click on Run the simulator will run + automatically, making random choices for you. If you are not sure what to + experiment with, this is an easy way of seeing the protocol in action. While + this is happening, the Run button changes to + Stop. Click on Stop to return the simulator to user + control. You can continue at this point as if you had made all the automatic + choices yourself. +

+ +

+ The Print, Load and Save buttons are disabled + since these simulations are running as applets. If you wish hard copy of the + simulation, position the scroll bar in the simulation pane at an interesting + point and print out the whole web page. If you have the code for the + simulator, you can run it as an application to enable these buttons. + Print produces a hard copy of the current simulation scenario. + Load loads a scenario file (with a name ending in + .scn). This must be for the same protocol as you are currently + simulating. It replaces the current simulation scenario (if any). + Save saves the current simulation scenario as a scenario file + (with a name ending in .scn). If you are adventurous, you can + create and edit your own scenarios using a standard text editor. +

+ +

Protocol Descriptions

+ +

+ The available simulations vary quite widely, though they all behave in a + similar way. The terminology used to describe each protocol is that of its + standard. The term "octet" is commonly used in international communications + standards to mean eight bits, i.e. a byte. +

+ +

+ Some simulations have associated protocol parameters. To change the defaults + that are shown, enter new values and click Change Values. In some + cases, this will force the simulation to restart. +

+ + + + + diff --git a/lab8_protocols/jasper/html/jasper.html b/lab8_protocols/jasper/html/jasper.html new file mode 100755 index 0000000..477f6c0 --- /dev/null +++ b/lab8_protocols/jasper/html/jasper.html @@ -0,0 +1,178 @@ + + + + + + + + + Protocol Simulators + + + + + + + +
+ +

Protocol Simulators

+ + Simulator Logo + +
+ +

Available Simulations

+ +

+ The following protocol simulations were developed mainly by Iain Robin and + Ken Turner, + with contributions from Paul Johnson and Kenneth Whyte. They should be + useful in allowing you to experiment with a variety of protocols. Bug + reports should be sent to Ken Turner. +

+ +

+ The simulations are listed in increasing order of complexity. It is + suggested that you try the easier protocols first. +

+ +
+ + Orange Dot + Alternating Bit Protocol (ABP) Simulator + +
Orange Dot + User Datagram Protocol (UDP) Simulator + +
Orange Dot + Boot Protocol (BOOTP) Simulator + +
Orange Dot + HyperText Transfer Protocol (HTTP) Simulator + +
Orange Dot + Simple Mail Transfer Transfer Protocol (SMTP) + Simulator + +
Orange Dot + Internet Protocol (IP) Simulator + +
Orange Dot + Sliding Window Protocol (SWP) 3-Column Simulator + +
Orange Dot + Sliding Window Protocol (SWP) 5-Column Simulator + +
Orange Dot + Abracadabra Protocol Simulator + +
Orange Dot + Trivial File Transfer Protocol (TFTP) Simulator + +
Orange Dot + Transmission Control Protocol (TCP) Client-Server + Simulator + +
Orange Dot + Transmission Control Protocol (TCP) Peer-Peer + Simulator + +
+ +

Additional Simulations

+ +

+ The following protocol simulations were developed by Ken Turner for + Pearson Education. Bug reports should be sent to Ken Turner. +

+ +

+ The simulations are listed in increasing order of complexity. It is + suggested that you try the easier protocols first. +

+ +
+ + Orange Dot + Protocol Stack (STACK) Simulator + +
Orange Dot + Multiplexing (MUX) Simulator + +
Orange Dot + Multicasting (MCAST) Simulator + +
Orange Dot + Carrier Sense Multiple Access with Collision Detection + (CSMA/CD, Ethernet) Simulator + +
Orange Dot + Transmission Control Protocol (TCP) Slow Start + Simulator + +
+ +

General Instructions

+ +

+ Select an action by clicking on the list at the bottom of the diagram. + Actions are things like a user sending a data message or the medium + delivering a protocol message. You are completely in control of the + simulation. For example you may have to decide when to send messages, + whether to acknowledge messages, and whether messages are lost in the medium + or are delivered. Since the simulation does not run in real-time, a timeout + is possible as soon as a message has been sent. +

+ +

+ The last action in the diagram is shown in red. If you make a mistake, or + just want to backtrack in the simulation, then click Undo. You can + undo as many steps as you like, right up to the beginning of the simulation. + Clicking on Redo will perform the last undone step again. + Clear will restart the simulation with the current protocol + parameters. If you click on Run the simulator will run + automatically, making random choices for you. If you are not sure what to + experiment with, this is an easy way of seeing the protocol in action. While + this is happening, the Run button changes to + Stop. Click on Stop to return the simulator to user + control. You can continue at this point as if you had made all the automatic + choices yourself. +

+ +

+ The Print, Load and Save buttons are disabled + since these simulations are running as applets. If you wish hard copy of the + simulation, position the scroll bar in the simulation pane at an interesting + point and print out the whole web page. If you have the code for the + simulator, you can run it as an application to enable these buttons. + Print produces a hard copy of the current simulation scenario. + Load loads a scenario file (with a name ending in + .scn). This must be for the same protocol as you are currently + simulating. It replaces the current simulation scenario (if any). + Save saves the current simulation scenario as a scenario file + (with a name ending in .scn). If you are adventurous, you can + create and edit your own scenarios using a standard text editor. +

+ +

Protocol Descriptions

+ +

+ The available simulations vary quite widely, though they all behave in a + similar way. The terminology used to describe each protocol is that of its + standard. The term "octet" is commonly used in international communications + standards to mean eight bits, i.e. a byte. +

+ +

+ Some simulations have associated protocol parameters. To change the defaults + that are shown, enter new values and click Change Values. In some + cases, this will force the simulation to restart. +

+ + + + + diff --git a/lab8_protocols/jasper/html/multicast.gif b/lab8_protocols/jasper/html/multicast.gif new file mode 100755 index 0000000..ad8ebee Binary files /dev/null and b/lab8_protocols/jasper/html/multicast.gif differ diff --git a/lab8_protocols/jasper/html/multiplexer.gif b/lab8_protocols/jasper/html/multiplexer.gif new file mode 100755 index 0000000..e4b12b0 Binary files /dev/null and b/lab8_protocols/jasper/html/multiplexer.gif differ diff --git a/lab8_protocols/jasper/html/simulator.gif b/lab8_protocols/jasper/html/simulator.gif new file mode 100755 index 0000000..fdde6db Binary files /dev/null and b/lab8_protocols/jasper/html/simulator.gif differ diff --git a/lab8_protocols/jasper/html/simulator.jpeg b/lab8_protocols/jasper/html/simulator.jpeg new file mode 100755 index 0000000..3475f2a Binary files /dev/null and b/lab8_protocols/jasper/html/simulator.jpeg differ diff --git a/lab8_protocols/jasper/html/uparrow.gif b/lab8_protocols/jasper/html/uparrow.gif new file mode 100755 index 0000000..c7587da Binary files /dev/null and b/lab8_protocols/jasper/html/uparrow.gif differ diff --git a/lab8_protocols/jasper/scenarios/TCPss.scn b/lab8_protocols/jasper/scenarios/TCPss.scn new file mode 100755 index 0000000..1bb1c05 --- /dev/null +++ b/lab8_protocols/jasper/scenarios/TCPss.scn @@ -0,0 +1,41 @@ +Jasper TCP/ss +User A: Send 800 octets +Medium: Deliver Seq 0, Win 1200 [Protocol A] - no loss +Medium: Deliver Ack 200, Win 1000 [Protocol B] - no loss +Protocol A: Send 400 octets to peer +Medium: Deliver Seq 200, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 400, Win 1200 [Protocol A] - no loss +Medium: Deliver Ack 400, Win 800 [Protocol B] - no loss +Medium: Deliver Ack 600, Win 600 [Protocol B] - no loss +Protocol B: Deliver 600 octets to user +Medium: Deliver Win 1200 [Protocol B] - no loss +Medium: Deliver Ack 100, Win 1200 [Protocol A] - no loss +User A: Send 800 octets +Medium: Deliver Seq 600, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 800, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 1000, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 1200, Win 1200 [Protocol A] - no loss +Medium: Deliver Ack 800, Win 1000 [Protocol B] - no loss +Medium: Deliver Ack 1000, Win 800 [Protocol B] - no loss +Medium: Deliver Ack 1200, Win 600 [Protocol B] - no loss +Medium: Deliver Ack 1400, Win 400 [Protocol B] - no loss +Protocol B: Deliver 800 octets to user +Medium: Deliver Win 1200 [Protocol B] - no loss +Medium: Deliver Ack 100, Win 1200 [Protocol A] - no loss +User A: Send 800 octets +Medium: Deliver Seq 1400, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 1600, Win 1200 [Protocol A] - no loss +Medium: Deliver Seq 1800, Win 1200 [Protocol A] - no loss +Medium: Lose Ack 2000, Win 600 [Protocol B] - congestion/error +Medium: Deliver Ack 1600, Win 1000 [Protocol B] - no loss +Medium: Lose Seq 2000, Win 1200 [Protocol A] - congestion/error +Protocol A: Timeout - resend Seq 1600, Win 1200 +Medium: Deliver Seq 1600, Win 1200 [Protocol A] - no loss +Medium: Deliver Ack 1800, Win 800 [Protocol B] - no loss +Medium: Deliver Ack 2000, Win 600 [Protocol B] - no loss +Protocol B: Deliver 600 octets to user +Medium: Deliver Win 1200 [Protocol B] - no loss +Medium: Deliver Ack 100, Win 1200 [Protocol A] - no loss +Protocol A: Timeout - resend Seq 2000, Win 1200 +Medium: Deliver Seq 2000, Win 1200 [Protocol A] - no loss +Medium: Deliver Ack 2200, Win 1000 [Protocol B] - no loss diff --git a/lab8_protocols/jasper/source/protocol/ABP.java b/lab8_protocols/jasper/source/protocol/ABP.java new file mode 100755 index 0000000..6257ad9 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABP.java @@ -0,0 +1,25 @@ +// ABRAServiceEntity.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class ABP extends Protocol { + + private ABPSender sender; + private ABPReceiver receiver; + + public ABP() { + medium = new Medium(); + sender = new ABPSender(medium, "Sender"); + receiver = new ABPReceiver(medium, "Receiver"); + sender.setPeer(receiver); + receiver.setPeer(sender); + entities = new Vector(); + entities.addElement(sender); + entities.addElement(medium); + entities.addElement(receiver); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/ABPReceiver.java b/lab8_protocols/jasper/source/protocol/ABPReceiver.java new file mode 100755 index 0000000..5288159 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABPReceiver.java @@ -0,0 +1,86 @@ +// ABPReceiver.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package protocol; + +import java.util.Enumeration; // enumeration +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +public class ABPReceiver implements ProtocolEntity { + + private int seqExpected; + private PDU pduReceived; + private PDU ackPDU; + private ProtocolEntity peer; + private Medium medium; + private String name; + private Vector entityEvents; // events from entity + + public ABPReceiver(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + if (pduReceived != null && pduReceived.type.equals("DATA")) { + if (pduReceived.seq == seqExpected) seqExpected = inc(seqExpected); + list.addElement( + "Send ACK(" + seqExpected + + ") - with sequence number of next message"); + } + return(list); + } + + /** Increment PDU sequence number (0 or 1 only in ABP protocol) */ + + private int inc(int seq) { + return(1 - seq); + } + + public void initialise() { + seqExpected = 0; + pduReceived = null; + entityEvents = new Vector(); // empty entity events + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith("Send ACK")) { + int startIndex = s.indexOf( '(' ) + 1; + int endIndex = s.indexOf( ')' ); + int seq = Integer.parseInt(s.substring(startIndex, endIndex)); + transmitPDU(new PDU("ACK", seq), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, ackPDU)); + } + for (Enumeration e = entityEvents.elements(); // get medium events + e.hasMoreElements(); ) + events.addElement( // add medium event + (ProtocolEvent) e.nextElement()); + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + ackPDU = pdu; + entityEvents = medium.receivePDU(pdu); + pduReceived = null; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/ABPSender.java b/lab8_protocols/jasper/source/protocol/ABPSender.java new file mode 100755 index 0000000..ba40903 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABPSender.java @@ -0,0 +1,119 @@ +// ABPSender.java + +package protocol; // protocol package + +import java.util.Enumeration; // enumeration +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +/** + This is the class for the alternating bit protocol sender. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +public class ABPSender implements ProtocolEntity, Timeouts { + + private PDU pduBeingSent; + private PDU pduReceived; + private ProtocolEntity peer; + private Medium medium; + private String name; + private boolean timerEnabled; + private Vector entityEvents; // events from entity + + public ABPSender(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + if (pduReceived != null && pduReceived.type.equals("ACK")) { + list.addElement("Send DATA(" + pduReceived.seq + ")"); + timerEnabled = false; + } + if (timerEnabled) + list.addElement("Timeout - presume loss of message and resend"); + return(list); + } + + public boolean hasTimer(String type) { + return(true); + } + + /** Increment PDU sequence number (0 or 1 only in ABP protocol) */ + + private int inc(int seq) { + return(1 - seq); + } + + public void initialise() { + // dummy initial data PDU + pduBeingSent = new PDU("DATA", 1); + pduBeingSent.setSource(this); + // dummy initial ack PDU + pduReceived = new PDU("ACK", 0); + timerEnabled = false; + entityEvents = new Vector(); // empty medium events + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith("Send DATA")) { + // PDU seq number delimited by brackets: + int startIndex = s.indexOf( '(' ) + 1; + int endIndex = s.indexOf( ')' ); + int seq = Integer.parseInt(s.substring(startIndex, endIndex)); + transmitPDU(new PDU("DATA", seq), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduBeingSent)); + } + if (s.startsWith("Timeout")) { + transmitPDU(pduBeingSent, peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduBeingSent)); + } + for (Enumeration e = entityEvents.elements(); // get medium events + e.hasMoreElements(); ) + events.addElement( // add medium event + (ProtocolEvent) e.nextElement()); + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + timerEnabled = enabled; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + pduBeingSent = pdu; + entityEvents = medium.receivePDU(pdu); // medium receives PDU + pduReceived = null; + timerEnabled = false; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/ABRA.java b/lab8_protocols/jasper/source/protocol/ABRA.java new file mode 100755 index 0000000..b96df54 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABRA.java @@ -0,0 +1,35 @@ +// ABRA.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class ABRA extends Protocol { + + private ABRAService abraServA; + private ABRAProtocol abraProtA; + private ABRAProtocol abraProtB; + private ABRAService abraServB; + + public ABRA() { + medium = new Medium(); + abraServA = new ABRAService("User A"); + abraServB = new ABRAService("User B"); + abraProtA = new ABRAProtocol(medium, "Protocol A"); + abraProtB = new ABRAProtocol(medium, "Protocol B"); + abraServA.setProvider(abraProtA); + abraServB.setProvider(abraProtB); + abraProtA.setUser(abraServA); + abraProtA.setPeer(abraProtB); + abraProtB.setUser(abraServB); + abraProtB.setPeer(abraProtA); + entities = new Vector(); + entities.addElement(abraServA); + entities.addElement(abraProtA); + entities.addElement(medium); + entities.addElement(abraProtB); + entities.addElement(abraServB); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/ABRAProtocol.java b/lab8_protocols/jasper/source/protocol/ABRAProtocol.java new file mode 100755 index 0000000..fc34cd0 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABRAProtocol.java @@ -0,0 +1,386 @@ +// ABRAProtocol.java + +package protocol; + +import java.util.*; +import support.*; + +/** + This is the class for the Abracadabra protocol sender. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +public class ABRAProtocol implements ProtocolEntity, Timeouts { + + // Protocol state constants + private static final int CLOSED = 0; // waiting for connection request + private static final int CR_SENT = 1; // waiting for connection confirmation + private static final int CR_RECV = 2; // waiting for ConResp from user + private static final int SEND = 3; // prepared to send data PDU + private static final int WAIT = 4; // waiting for acknowledgement of data + private static final int DR_SENT = 5; // waiting for disconnection confirm + + // Protocol data unit types + public static final String CR = "Connection Request"; + public static final String CC = "Connection Confirmation"; + public static final String DT = "Data Transfer"; + public static final String AK = "Acknowledgement"; + public static final String DR = "Disconnection Request"; + public static final String DC = "Disconnection Confirmation"; + + public static final int maxRetries = 3; + + private PDU pduSent; + private PDU lastDTpdu; + private int sendSeq; + private int recvSeq; + private ProtocolEntity peer; + private ABRAService user; + private Vector entityEvents; // events from entity + private Medium medium; + private String name; + private boolean timerEnabled; + private int crRemaining; + private int drRemaining; + private int dtRemaining; + private boolean dtOrAk; + private int state; + + public ABRAProtocol(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + switch (state) { + case CR_SENT: // waiting for connection confirmation + if (timerEnabled) { + if (crRemaining > 1) + list.addElement("Timeout - resend " + CR); + else + list.addElement("Retry limit reached - send " + DR); + } + break; + case WAIT: // waiting for acknowledgement of data + if (timerEnabled) { + if (dtRemaining > 1) + list.addElement("Timeout - resend DT(" + lastDTpdu.seq + ")" ); + else + list.addElement("Retry limit reached - send " + DR); + } + break; + case DR_SENT: // waiting for disconnection confirmation + if (timerEnabled) { + if (drRemaining > 1) { + list.addElement("Timeout - resend " + DR); + } + else { + // Retry limit reached + dtRemaining--; + state = CLOSED; + } + } + break; + } + return(list); + } + + /** Increment PDU sequence number (alternating bit) */ + + private int inc(int seq) { + return(1 - seq); + } + + public void initialise() { + entityEvents = new Vector(); + state = CLOSED; + } + + public boolean hasTimer(String type) { + return(type.equals("DT") || type.equals("DR") || type.equals("CR")); + } + + public Vector performService(String s) { + Vector events = new Vector(); + switch (state) { + case CR_SENT: + if (s.startsWith("Timeout")) { + crRemaining--; + transmitPDU(new PDU("CR"), peer); // resend connection request PDU + events.addElement( + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + if (s.startsWith("Retry limit")) { + crRemaining--; + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DR"), peer); // send disconnection request PDU + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + break; + case WAIT: + if (s.startsWith("Timeout")) { + dtRemaining--; + transmitPDU(lastDTpdu, peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + if (s.startsWith("Retry limit")) { + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + dtRemaining--; + transmitPDU(new PDU("DR"), peer); // send disconnection request PDU + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + break; + case DR_SENT: + if (s.startsWith("Timeout")) { + drRemaining--; + transmitPDU(new PDU("DR"), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + break; + } + for (Enumeration e = entityEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + String pduType = ""; + if (pdu != null) + pduType = pdu.type; + switch (state) { + case CLOSED: + if (pduType.equals("ConReq")) { // ConReq primitive? + transmitPDU(new PDU("CR"), peer); // send connection request PDU + dtOrAk = false; + sendSeq = 0; + recvSeq = 0; + crRemaining = maxRetries; + state = CR_SENT; + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + } + else if (pduType.equals("CR")) { // connection request PDU? + transmitPDU(new PDU("ConInd"), user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + dtOrAk = false; + sendSeq = 0; + recvSeq = 0; + state = CR_RECV; + } + else if (pduType.equals("DR")) { // got disconnect request PDU + transmitPDU(new PDU("DC"), peer); // send disconnect confirm PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + } + break; + case CR_SENT: + if (pduType.equals("CC") || // connection confirmation PDU? + pduType.equals("CR")) { // connection request PDU? + // (both stations have requested connections at the same time) + transmitPDU(new PDU("ConConf"), user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + timerEnabled = false; + state = SEND; + } + else if (pduType.equals("DR")) { // disconnection request PDU? + transmitPDU(new PDU("DisInd"), user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + timerEnabled = false; + state = CLOSED; + } + else if (pduType.equals("DisReq")) { // disconnection request prim? + // transmitPDU(new PDU("DisInd"), user); + // events.addElement( + // new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DR"), peer); // send disconnect request PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + break; + case CR_RECV: + if (pduType.equals("ConResp")) { + transmitPDU(new PDU("CC"), peer); // send connection confirm PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + state = SEND; + } + else if (pduType.equals("DR")) { + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DC"), peer); // send disconnection confirm PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + state = CLOSED; + } + else if (pduType.equals("ConReq")) { + transmitPDU(new PDU("ConConf"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("CC"), peer); // send connection confirm PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + state = SEND; + } + else if (pduType.equals("DisReq")) { + transmitPDU(new PDU("DR"), peer); // send disconnect request PDU + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + state = CLOSED; + } + break; + case WAIT: + if (pduType.equals("AK")) { + if (pdu.seq == sendSeq) { + dtOrAk = true; + timerEnabled = false; + user.setOKToSend(true); + state = SEND; + } + else { // wrong AK : enter error phase + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DR"), peer); // send disconnect request PDU + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + } + // break intentionally omitted + case SEND: + if (state == SEND // avoid drop-through from WAIT + && pduType.equals("DatReq")) { // DatReq received from user + transmitPDU(new PDU("DT", sendSeq, pdu.getSDU()), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + user.setOKToSend(false); + sendSeq = inc(sendSeq); + dtRemaining = maxRetries; + state = WAIT; + } + else if (pduType.equals("DR")) { // disconnection request PDU? + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DC"), peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + timerEnabled = false; + state = CLOSED; + } + else if (pduType.equals("DT")) { // data PDU received + dtOrAk = true; + if (pdu.seq == recvSeq) { // check sequence number correct + recvSeq = inc(recvSeq); + transmitPDU(new PDU("DatInd", pdu.getSDU()), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + } + transmitPDU(new PDU("AK", recvSeq), peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + // no state transition + } + if (pduType.equals("CC") || pduType.equals("DC")) { + transmitPDU(new PDU("DisInd"), user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DR"), peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + if (pduType.equals("CR")) { // connection request PDU? + if (!dtOrAk) { + transmitPDU(new PDU("CC"), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + // no state transition + } + else { + transmitPDU(new PDU("DisInd"), user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + transmitPDU(new PDU("DR"), peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + } + if (pduType.equals("DisReq")) { + transmitPDU(new PDU("DR"), peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + drRemaining = maxRetries; + state = DR_SENT; + } + break; + case DR_SENT: // awaiting disconnect confirm + if (pduType.equals("DC") || // disconnect confirm PDU? + pduType.equals("DR")) { // disconnect request PDU? + // both stations have requested disconnection at the same time + timerEnabled = false; + state = CLOSED; + } + if (pduType.equals("ConReq")) { + transmitPDU(new PDU("DisInd"), user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + } + if (timerEnabled && drRemaining <= 1) { + drRemaining--; + state = CLOSED; + } + break; + } + return(events); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + timerEnabled = enabled; + } + + public void setUser(ProtocolEntity user) { + this.user = (ABRAService)user; + } + + public void transmitPDU(PDU pdu, ProtocolEntity destination) { + pdu.setSource(this); + pdu.setDestination(destination); + if (destination == peer) + entityEvents = medium.receivePDU(pdu); + else + entityEvents = user.receivePDU(pdu); + pduSent = pdu; + if (pdu.type.equals("DT")) + lastDTpdu = pdu; +for (Enumeration e = entityEvents.elements(); e.hasMoreElements(); ) + System.err.println("ABRAP transmitPDU: " + (ProtocolEvent) e.nextElement()); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/ABRAService.java b/lab8_protocols/jasper/source/protocol/ABRAService.java new file mode 100755 index 0000000..90e1a6c --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/ABRAService.java @@ -0,0 +1,171 @@ +// ABRAService.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class ABRAService implements ProtocolEntity { + + // Service state constants + private static final int DISCONNECTED = 0; // no connection in progress + // or active + private static final int CALLING = 1; // connection initiated by local + // user is being established + private static final int CALLED = 2; // connection initiated by remote + // user is being established + private static final int CONNECTED = 3; // data transfer is possible + + // Service primitives + public static final String CON_REQ = "Connection Request"; + public static final String CON_IND = "Connection Indication"; + public static final String CON_RESP = "Connection Response"; + public static final String CON_CONF = "Connection Confirmation"; + public static final String DAT_REQ = "Data Request"; + public static final String DAT_IND = "Data Indication"; + public static final String DIS_REQ = "Disconnection Request"; + public static final String DIS_IND = "Disconnection Indication"; + + private ProtocolEntity provider; // protocol providing service + private Vector providerEvents; + private PDU pduSent; + private String name; + private int state; + private int block; // sequence number of data block + private boolean okToSend; // OK to send DatReq to provider + + public ABRAService(String name) { + this.name = name; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + switch (state) { + case DISCONNECTED: + list.addElement("Send " + CON_REQ); + break; + case CALLING: + list.addElement("Send " + DIS_REQ); + break; + case CALLED: + list.addElement("Send " + CON_RESP); + list.addElement("Send " + DIS_REQ); + break; + case CONNECTED: + if (okToSend) + list.addElement("Send " + DAT_REQ + "(D" + block + ")"); + list.addElement("Send " + DIS_REQ); + break; + } + return(list); + } + + public void initialise() { + block = 0; + okToSend = true; + providerEvents = new Vector(); + state = DISCONNECTED; + } + + public Vector performService(String s) { + Vector events = new Vector(); + switch (state) { + case DISCONNECTED: + if (s.equals("Send " + CON_REQ)) { + transmitPDU(new PDU("ConReq"), provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + state = CALLING; + } + case CALLING: + if (s.equals("Send " + DIS_REQ)) { + transmitPDU(new PDU("DisReq"), provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + state = DISCONNECTED; + } + break; + case CALLED: + if (s.equals("Send " + CON_RESP)) { + transmitPDU(new PDU("ConResp"), provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + state = CONNECTED; + } + if (s.equals("Send " + DIS_REQ)) { + transmitPDU(new PDU("DisReq"), provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + state = DISCONNECTED; + } + break; + case CONNECTED: + if (s.startsWith("Send " + DAT_REQ)) { + String sdu = "D" + block; + transmitPDU(new PDU("DatReq", sdu), provider); + block++; + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + // No state transition + } + if (s.equals("Send " + DIS_REQ)) { + transmitPDU(new PDU("DisReq"), provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.SEND, pduSent)); + state = DISCONNECTED; + } + } + for (Enumeration e = providerEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + + /** Respond to PDU received from underlying protocol */ + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + String pduType = pdu.type; + switch (state) { + case DISCONNECTED: + if (pduType.equals("ConInd")) state = CALLED; + break; + case CALLING: + // Is the ConInd possibility below needed? + if (pduType.equals("ConConf") + || pduType.equals("ConInd")) state = CONNECTED; + if (pduType.equals("DisInd")) state = DISCONNECTED; + break; + case CALLED: + if (pduType.equals("DisInd")) state = DISCONNECTED; + break; + case CONNECTED: + if (pduType.equals("DisInd")) state = DISCONNECTED; + break; + } + return(events); + } + + /** Implements flow control by back-pressure. DatReq allowed only when + okToSend is true */ + + public void setOKToSend(boolean ok) { + okToSend = ok; + } + + public void setProvider(ProtocolEntity provider) { + this.provider = provider; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + providerEvents = dest.receivePDU(pdu); + pduSent = pdu; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/BOOTP.java b/lab8_protocols/jasper/source/protocol/BOOTP.java new file mode 100755 index 0000000..b1924c7 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/BOOTP.java @@ -0,0 +1,29 @@ +// BOOTP.java (C) K. J. Turner, K. A. Whyte 04/03/06 + +// Boot Protocol + +package protocol; // protocol package + +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +public class BOOTP extends Protocol { // BOOTP protocol + + private BOOTPSender sender; // protocol sender (client) + private BOOTPReceiver receiver; // protocol receiver (server) + + public BOOTP() { // construct protocol instance + medium = new BOOTPMedium(); // construct comms medium + sender = // construct sender (client) + new BOOTPSender (medium, "Client"); + receiver = // construct receiver (server) + new BOOTPReceiver (medium, "Server"); + sender.setPeer(receiver); // sender is receiver's peer + receiver.setPeer(sender); // receiver is sender's peer + entities = new Vector(); // initialise protocol entities + entities.addElement(sender); // add sender protocol entity + entities.addElement(medium); // add comms medium entity + entities.addElement(receiver); // add receive protocol entity + } + +} diff --git a/lab8_protocols/jasper/source/protocol/BOOTPMedium.java b/lab8_protocols/jasper/source/protocol/BOOTPMedium.java new file mode 100755 index 0000000..347e348 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/BOOTPMedium.java @@ -0,0 +1,75 @@ +// BOOTPMedium.java (C) K. J. Turner 08/03/06 + +// Boot Protocol medium + +package protocol; // protocol package + +import java.util.*; // utility support +import support.*; // protocol entity support + +/** + This is the class for an IP medium. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (24th July 2010, KJT): minor tidying +*/ +public class BOOTPMedium extends Medium { // protocol medium + + // protocol variables + + private static Vector randoms; // random number list + private static int randomIndex; // random number index + + public BOOTPMedium() { // construct medium instance + super(); // construct as generic medium + randoms = new Vector(); // initialise list of randoms + } + + protected PDU getMatchingPDU(String s) { // get matching PDU on channel + PDU pdu; // PDU + String sdu; // PDU contents + + int sourceStart = s.indexOf ('[') + 1; // get start of entity name + int sourceEnd = s.indexOf (']'); // get end of entity name + String sourceName = // get PDU source + s.substring(sourceStart, sourceEnd); + int typeStart = s.indexOf (' ') + 1; // get start of PDU type + int typeEnd = s.indexOf ('('); // get end of PDU type + String type = // get PDU type + s.substring(typeStart, typeEnd); + int parEnd = s.indexOf (')'); // get end of PDU parameters + sdu = s.substring(typeEnd + 1, parEnd); // get PDU parameters + for (Enumeration e = pdus.elements(); // go through PDUs on channel + e.hasMoreElements(); ) { + pdu = (PDU) e.nextElement(); // get next PDU on channel + if (pdu != null && // valid PDU and ... + pdu.type.equals(type) && // type matches and ... + pdu.getSource().getName(). // source matches and ... + equals(sourceName) && + sdu.equals(pdu.sdu)) // SDU matches + return((pdu)); // return with this PDU + } + return((null)); // return no PDU as no match + } + + public void initialise() { // initialise medium + super.initialise(); // initialise generic medium + randomIndex = 0; // initialise randoms index + } + + protected static float random() { // random number (from list) + Float rand; // random number + + if (randomIndex < randoms.size()) // get number from list? + rand = (Float) randoms.elementAt(randomIndex++); // get number from list + else { // make new random number + rand = new Float(Math.random()); // get random number + randoms.addElement(rand); // add to list + randomIndex++; // increment list index + } + return((rand.floatValue())); // return random number + } + +} diff --git a/lab8_protocols/jasper/source/protocol/BOOTPReceiver.java b/lab8_protocols/jasper/source/protocol/BOOTPReceiver.java new file mode 100755 index 0000000..b3b11a9 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/BOOTPReceiver.java @@ -0,0 +1,139 @@ +// BOOTPReceiver.java (C) K. A. Whyte, K. J. Turner 08/03/06 + +package protocol; // protocol package + +import java.util.Enumeration; // enumeration +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +public class BOOTPReceiver // protocol receiver (server) + implements ProtocolEntity { // protocol entity + + // simulator variables + + private ProtocolEntity peer; // peer entity (client) + private Medium medium; // communications medium + private String name; // entity name + + // protocol variables + + private String ipAddress = "net."; // IP address root + private String bootFile = "boot"; // boot file name + private String bootPath = "\\path\\"; // boot file root + private PDU pduSent; // PDU sent + private PDU pduReceived; // PDU received + private Vector entityEvents; // events from entity + + // protocol messages + + final static String request = "REQUEST"; // boot request message + final static String reply = "REPLY"; // boot reply message + + public BOOTPReceiver(Medium m, String name) { // construct receiver instance + this.name = name; // set protocol entity name + medium = m; // set underlying medium + initialise(); // initialise protocol + } + + public String getName() { // get protocol entity name + return((name)); // return protocol entity name + } + + public void initialise() { // initialise protocol + pduReceived = null; // initialise no PDU received + pduSent = null; // initialise no PDU sent + entityEvents = new Vector(); // empty medium events + } + + public Vector getServices() { + Vector events = // initialise events list + new Vector(); + String pduType; // received PDU type + String pduData; // PDU data + String pduTrans; // transaction identifier + int hwPos, ipPos, bootPos; // hw/ip/boot file positions + int trans; // transaction identifier + String hw, ip; // hardware/IP addr address + String boot; // boot file name + + if (pduReceived != null) { // PDU received? + pduType = pduReceived.type; // get received PDU type + pduData = pduReceived.sdu; // get received PDU data + if (pduType.equals(request)) { // boot request? + hwPos = pduData.indexOf(','); // get hardware position + trans = // get transaction identifier + Integer.parseInt(pduData.substring(0, hwPos)); + ipPos = pduData.indexOf(',', hwPos + 1); // get IP address position + if (ipPos == -1) { // no IP address parameter + hw = pduData.substring(hwPos + 1); // get hardware address + ip = // random IP address 10..99 + ipAddress + (10 + (int) (BOOTPMedium.random() * 99)); + boot = bootPath + bootFile; // set full boot file name + } + else { // IP address parameter + hw = // get hardware address + pduData.substring(hwPos + 1, ipPos); + bootPos = // get boot file position + pduData.indexOf(',', ipPos + 1); + if (bootPos == -1) { // no boot file parameter + ip = pduData.substring(ipPos + 1); // get IP address + boot = bootPath + bootFile; // set full boot file name + } + else { // boot file parameter + ip = // get IP address + pduData.substring(ipPos + 1, bootPos); + boot = // get boot file name + pduData.substring(bootPos + 1); + boot = bootPath + boot; // prefix with boot file path + } + } + events.addElement( // send reply + reply + "(" + trans + + "," + hw + "," + ip + "," + boot + + ") - reply with IP address and boot file path"); + } + } + return(events); + } + + public Vector performService(String s) { + Vector events = // initialise events list + new Vector(); + int start, middle, end; // start/middle/end positions + String pduData; // PDU data + + if (s.startsWith(reply)) { // reply? + start = s.indexOf('(') + 1; // get contents start + end = s.indexOf(')'); // get contents end + pduData = s.substring(start, end); // get data contents + transmitPDU( // send reply + new PDU(reply, pduData), peer); + events.addElement( // transmit PDU + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + } + for (Enumeration e = entityEvents.elements(); // go through medium events + e.hasMoreElements(); ) + events.addElement( // add medium event + (ProtocolEvent) e.nextElement()); + return(events); + } + + public Vector receivePDU(PDU pdu) { // handle received PDU + pduReceived = pdu; // store PDU + return((new Vector())); // return no events + } + + public void setPeer(ProtocolEntity peer) { // set protocol peer + this.peer = peer; // set this entity's peer + } + + public void transmitPDU( // transmit PDU + PDU pdu, ProtocolEntity dest) { // for given PDU, destination + pdu.setSource(this); // source is this entity + pdu.setDestination(dest); // destination is as given + pduSent = pdu; // copy PDU sent + entityEvents = medium.receivePDU(pdu); // medium receives PDU + pduReceived = null; // note no PDU in response + } + +} diff --git a/lab8_protocols/jasper/source/protocol/BOOTPSender.java b/lab8_protocols/jasper/source/protocol/BOOTPSender.java new file mode 100755 index 0000000..1700bff --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/BOOTPSender.java @@ -0,0 +1,177 @@ +// BOOTPSender.java (C) K. J. Turner, K. A. Whyte 08/03/06 + +// Boot Protocol sender (client) + +package protocol; // protocol package + +import java.util.Enumeration; // enumeration +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +/** + This is the class for a boot protocol sender. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): unchecked warning suppressed +*/ +public class BOOTPSender // protocol sender (client) + implements ProtocolEntity, Timeouts { // protocol entity, timeout + + // simulator variables + + private ProtocolEntity peer; // peer entity (client) + private Medium medium; // communications medium + private String name; // entity name + + // protocol variables + + private int transId; // transaction identifier + private String hwAddress = "hw"; // hardware address + private String ipAddress = "net.91"; // IP address + private String bootFile = "boot"; // boot file name + private PDU pduSent; // PDU sent + private PDU pduReceived; // PDU received + private boolean timerEnabled; // whether timer is enabled + private Vector entityEvents; // events from entity + + // protocol state + + int state; // current protocol state + + final static int idle = 0; // no request outstanding + final static int wait = 1; // waiting for reply + + // protocol messages + + final static String request = "REQUEST"; // boot request message + final static String reply = "REPLY"; // boot reply message + + // protocol methods + + public BOOTPSender (Medium m, String name) { // construct sender instance + this.name = name; // set protocol entity name + medium = m; // set underlying medium + initialise (); // initialise protocol + } + + public String getName () { // get protocol entity name + return((name)); // return protocol entity name + } + + public boolean hasTimer (String type) { // protocol uses timer? + return((true)); // report it does + } + + public void initialise() { // initialise protocol + state = idle; // initialise state + pduReceived = null; // initialise no PDU received + pduSent = null; // initialise no PDU sent + timerEnabled = false; // initialise no timeout + entityEvents = new Vector(); // empty medium events + } + + public void setPeer(ProtocolEntity peer) { // set protocol peer + this.peer = peer; // set this entity's peer + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + timerEnabled = enabled; // store timer status + } + + public Vector getServices() { + Vector events = // list of events + new Vector(); + String pduType; // received PDU type + String pduData; // PDU data + String pduTrans; // transaction identifier + int transPos, hwPos; // trans/hw address positions + int trans; // transaction identifier + + if (pduReceived != null) { // PDU received? + pduType = pduReceived.type; // get received PDU type + pduData = pduReceived.sdu; // get received PDU data + transPos = pduData.indexOf('(') + 1; // get contents start + hwPos = pduData.indexOf(','); // get transaction end + trans = // get transaction identifier + Integer.parseInt(pduData.substring(transPos, hwPos)); + if (pduType.equals(reply) && // reply message and ... + trans == transId) { // same transaction identifier? + timerEnabled = false; + state = idle; // no longer waiting + } + } + if (state == idle) { // not awaiting reply + trans = // make random id 10..99 + 10 + ((int) (BOOTPMedium.random() * 90)); + events.addElement( // send request for IP/file + request + "(" + trans + "," + + hwAddress + ") - send IP address and boot file path request"); + events.addElement( // send request for file + request + "(" + trans + "," + + hwAddress + "," + ipAddress + + ") - send boot file path request"); + events.addElement( // send request for file path + request + "(" + trans + "," + + hwAddress + "," + ipAddress + "," + + bootFile + ") - send path request for given boot file"); + } + if (timerEnabled) + events.addElement("Timeout - presume loss of message and resend"); + return(events); // return list of events + } + + public Vector performService(String s) { + Vector events = // initialise events list + new Vector(); + int start, middle, end; // start/mid/end positions + String pduData; // PDU data + + if (s.startsWith(request)) { + start = s.indexOf('(') + 1; // get contents start + middle = s.indexOf(','); // get trans ident end + end = s.indexOf(')' ); // get contents end + transId = // get transaction ident + Integer.parseInt(s.substring(start, middle)); + pduData = s.substring(start, end); // get SDU + transmitPDU( // send request + new PDU(request, pduData), peer); + events.addElement( // transmit PDU + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + state = wait; // awaiting reply + } + if (s.startsWith("Timeout")) { // timeout? + transmitPDU(pduSent, peer); // re-send PDU + events.addElement( // add timeout event and PDU + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + for (Enumeration e = entityEvents.elements(); // go through medium events + e.hasMoreElements(); ) + events.addElement( // add medium event + (ProtocolEvent) e.nextElement()); + return((events)); // return list of events + } + + public Vector receivePDU(PDU pdu) { // handle received PDU + pduReceived = pdu; // store PDU + return(new Vector()); // return no events + } + + public void transmitPDU( // transmit PDU + PDU pdu, ProtocolEntity dest) { // for given PDU, destination + pdu.setSource(this); // source is this entity + pdu.setDestination(dest); // destination is as given + pduSent = pdu; // copy PDU sent + entityEvents = medium.receivePDU(pdu); // medium receives PDU + pduReceived = null; // note no PDU in response + timerEnabled = false; // note no timeout + } + +} diff --git a/lab8_protocols/jasper/source/protocol/CSMA.java b/lab8_protocols/jasper/source/protocol/CSMA.java new file mode 100755 index 0000000..4e9522f --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMA.java @@ -0,0 +1,140 @@ +// CSMA.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of CSMA/CD. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010, KJT): initial version +*/ + +public class CSMA extends Protocol { + + /** Maximum protocol retry limit */ + private final static int MAX = 5; + + /** Default protocol retry limit (0 means no collisions or retries) */ + private final static int RETRIES = 0; + + /** MAC 1 */ + private CSMAProtocol protocolA; + + /** MAC 2 */ + private CSMAProtocol protocolB; + + /** Protocol retry limit (default 0, i.e. no collisions or retries) */ + protected static int retryLimit = RETRIES; + + /** System 1 */ + private CSMAService userA; + + /** System 2 */ + private CSMAService userB; + + /** Index into random numbers */ + protected static int randomIndex; + + /** List of generated random numbers */ + protected static Vector randomNumbers; + + /** + Constructor for a CSMA object. + */ + public CSMA() { + medium = new CSMAMedium("LAN"); // create Medium + userA = new CSMAService("Station 1"); // create Station 1 + userB = new CSMAService("Station 2"); // create Station 2 + protocolA = // create MAC 1 + new CSMAProtocol(medium, "MAC 1"); + protocolB = // create MAC 2 + new CSMAProtocol(medium, "MAC 2"); + + medium.setMediumType( // set medium delivery control + Medium.CONTROL_DELIVERY); + userA.setProvider(protocolA); // set Station 1 -> MAC 1 + userB.setProvider(protocolB); // set Station 2 -> MAC 2 + protocolA.setUser(userA); // set MAC 1 -> Station 1 + protocolA.setPeer(protocolB); // set MAC 1 -> MAC 2 + protocolB.setUser(userB); // set MAC 2 -> Station 2 + protocolB.setPeer(protocolA); // set MAC 2 -> MAC 1 + + entities = new Vector(); // create entity list + entities.addElement(userA); // add Station 1 + entities.addElement(protocolA); // add MAC 1 + entities.addElement(medium); // add Medium + entities.addElement(protocolB); // add MAC 2 + entities.addElement(userB); // add Station 2 + + randomNumbers = new Vector(); // initialise random numbers + } + + /** + Return a random float. If the list of randoms contains an unused value then + use this, otherwise generate a fresh value and add to the list. This allows + random numbers to be generated consistently when simulation steps are + undone/redone. + + @return random number in the range (0..1] + */ + protected static float getRandom() { + if (randomIndex < randomNumbers.size()) // index within list? + return( // return this random + ((Float)randomNumbers.elementAt(randomIndex++)).floatValue()); + else { // index not within list + Float random = new Float(Math.random()); // get random number + randomNumbers.addElement(random); // add random to list + randomIndex++; // increment list index + return(random.floatValue()); // return rand number + } + } + + /** + Return the random numbers maintained by this protocol. The numbers are + stored in a scenario file. + + @return random number list + */ + public Vector getRandomNumbers() { + return(randomNumbers); // return the random numbers + } + + /** + Set a parameter of the CSMA simulation. + + @param parameter parameter name (retryLimit) + @param value parameter value + */ + public void setParameter(String parameter, String value) { + try { + if (parameter.equals("retryLimit")) { // retry limit? + int count = Integer.parseInt(value); // get value as int (if any) + if (0 <= count && count <= MAX) // retry limit within range? + retryLimit = count; // set retry limit + else // retry limit out of range + System.err.println( // report error + "CSMA.setParameter: retry limit '" + value + "' out of range"); + } + } + catch (NumberFormatException e) { // retry limit format exception? + System.err.println( // report error + "CSMA.setParameter: retry limit '" + value + "' not an integer"); + } + } + + /** + Set the random numbers maintained by this protocol. The numbers are + retrieved from a scenario file. + + @param randoms random number list + */ + public void setRandomNumbers(Vector randoms) { + randomNumbers = randoms; // store the random numbers + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/CSMAMedium.java b/lab8_protocols/jasper/source/protocol/CSMAMedium.java new file mode 100755 index 0000000..d8d0b29 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMAMedium.java @@ -0,0 +1,105 @@ +// CSMAMedium.java + +package protocol; // protocol package + +import java.util.Enumeration; // import Java enumerations +import java.util.HashSet; // import Java hash sets +import java.util.StringTokenizer; // import Java string tokenisers +import java.util.Vector; // import Java vectors + +import support.*; // import Jasper support classes + +/** + This is the class that defines a CSMA/CD medium. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010): initial version +*/ + +public class CSMAMedium extends Medium { + + /** Medium deliver offer */ + private final static String DELIVER = "Deliver "; + + /** Medium name */ + private String mediumName; + + /** + Constructor for a CSMA/CD medium. + + @param mediumName medium name + */ + public CSMAMedium(String mediumName) { + this.mediumName = mediumName; // set protocol name + } + + /** + Return the PDU currently with the same destination and SDU, in a format such + as "START(D1) to MAC 1". + + @param description service description + @return corresponding PDU (or null if not found) + */ + + protected PDU getMatchingPDU(String description) { + description = // remove "Deliver " + description.substring(DELIVER.length()); + for (Enumeration enumeration = pdus.elements(); + enumeration.hasMoreElements(); ) { // go through PDUs + CSMAPdu pdu = // get next PDU + (CSMAPdu) enumeration.nextElement(); + if (pdu.getDescription().equals(description)) // matching PDU found? + return(pdu); // return matched PDU + } + return(null); // return no PDU + } + + /** + Get the medium name. + + @return medium name + */ + public String getName() { + return(mediumName); // return protocol name + } + + /** + Return a list of strings describing actions (services) that can be carried + out in the current state. + + @return service descriptions + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + for (Enumeration enumeration = pdus.elements(); + enumeration.hasMoreElements(); ) { // go through PDUs in medium + PDU pdu = (PDU) enumeration.nextElement();// get PDU + if (pdu != null) { // non-null PDU? + String description = pdu.getID(); // get PDU id + String destination = // get PDU destination name + pdu.getDestination().getName(); + if (!contains(destination, list)) // nothing for this destination? + list.addElement(DELIVER + description + " to " + destination); + } + } + return(list); + } + + /** + Check if the given string in contained in the given list. + + @param string string to search for + @param list list to search + @return true/false if the medium has no/has PDUs + */ + public boolean contains(String string, Vector list) { + for (int i = 0; i < list.size(); i++) { // go through list + String text = (String) list.get(i); // get list text + if (text.indexOf(string) >= 0) // text contains string? + return(true); // return true immediately + } + return(false); // return false as no match + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/CSMAPdu.java b/lab8_protocols/jasper/source/protocol/CSMAPdu.java new file mode 100755 index 0000000..b289370 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMAPdu.java @@ -0,0 +1,67 @@ +// CSMAPdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines CSMA/CD protocol messages. + + @author Kenneth J. Turner + @version 1.0 (27th July 2010, KJT): initial version +*/ +public class CSMAPdu extends PDU { + + /** Protocol transmission finished PDU */ + public final static String FINISH = "FINISH"; + + /** Protocol transmission collision PDU */ + public final static String JAM = "JAM"; + + /** Protocol transmission started PDU */ + public final static String START = "START"; + + /** + Constructor for a CSMA/CD PDU for the given type and with the given SDU. + + @param type PDU type + @param sdu PDU payload as SDU + */ + public CSMAPdu(String type, String sdu) { + super(type, sdu); // construct PDU + } + + /** + Return the PDU description with destination and SDU, in a format such as + "START(D1) to MAC 1". + + @return PDU description + */ + public String getDescription() { + return( + getLabel() + " to " + getDestination().getName()); + } + + /** + Return the label for an arrow representing a CSMA/CD PDU in a time sequence + diagram. + + @return PDU label + */ + public String getLabel() { + return( // return PDU as string + sdu == null || sdu.length() == 0 ? type : type + "(" + sdu + ")"); + } + + /** + Convert PDU to string. + + @return PDU as string + */ + public String toString() { + return( // return PDU as string + sdu == null || sdu.length() == 0 ? type : type + "(" + sdu + ")"); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/CSMAProtocol.java b/lab8_protocols/jasper/source/protocol/CSMAProtocol.java new file mode 100755 index 0000000..ca435c2 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMAProtocol.java @@ -0,0 +1,414 @@ +// CSMAProtocol.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the CSMA/CD MAC layer. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010, KJT): initial version +*/ + +public class CSMAProtocol implements ProtocolEntity { + + ////////////////////////// User Interface Messages /////////////////////////// + + /** Protocol deliver offer */ + private final static String DELIVER = "Deliver data to "; + + /** Protocol finish transmission offer */ + private final static String FINISH = "Finish sending data to "; + + /** Protocol jam offer */ + private final static String JAM = "Jam transmission to "; + + /** Protocol retry limit report */ + private final static String RETRIES = + "Report failure due to excessive retries"; + + /** Protocol start transmission offer */ + private final static String START = "Start sending data to "; + + ////////////////////////////// Protocol States /////////////////////////////// + + /** Protocol state - collision */ + protected final static int NORMAL = 0; + + /** Protocol state - collision detected */ + protected final static int COLLISION = 1; + + /** Protocol state - jam received following collision */ + protected final static int JAM_IN = 2; + + /** Protocol state - jam sent following collision */ + protected final static int JAM_OUT = 3; + + /** Protocol state - backing off following jam */ + protected final static int BACKOFF = 4; + + ///////////////////////////// Protocol Variables ///////////////////////////// + + /** Protocol backoff count */ + protected int protocolBackoff; + + /** Protocol deferring to an incoming transmission */ + private boolean protocolDeferring; + + /** Protocol events */ + private Vector protocolEvents; + + /** Protocol PDU received */ + protected CSMAPdu protocolIn; + + /** Protocol channel */ + protected CSMAMedium protocolMedium; + + /** Protocol name */ + protected String protocolName; + + /** Protocol PDU sent */ + protected CSMAPdu protocolOut; + + /** Protocol peer */ + protected CSMAProtocol protocolPeer; + + /** Protocol in the process of receiving a message */ + protected boolean protocolReceiving; + + /** Protocol retry count */ + protected int protocolRetries; + + /** Protocol in the process of sending a message */ + protected boolean protocolSending; + + /** Protocol collision state */ + protected int protocolState; + + /** Protocol user */ + protected CSMAService protocolUser; + + ////////////////////////////// Protocol Methods ////////////////////////////// + + /** + Constructor for a (de)multiplexer. + + @param protocolMedium channel + @param protocolName protocol name + */ + public CSMAProtocol(Medium protocolMedium, String protocolName) { + this.protocolName = protocolName; // set protocol name + this.protocolMedium = // set protocol medium + (CSMAMedium) protocolMedium; + initialise(); // initialise protocol + } + + /** + Return the backoff count as a random integer in the range (0, 2^retries]. + This is 0..1 if there have been no retries, 0..2 if there has been one + retry, etc. Technically this is the number of slot times, but actual time is + irrelevant in the simulation approach. + + @return backoff interval + */ + public int backoffCount() { + float backoff = // get random in (0, 2^retries] + (float) (CSMA.getRandom() * Math.pow(2.0, protocolRetries)); + return(Math.round(backoff)); // return nearest integer + } + + /** + Return a protocol event with the given comment. + + @param comment protocol comment + @return protocol event + */ + public ProtocolEvent comment(String comment) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, comment)); + } + + /** + Get the protocol name. + + @return protocol name + */ + public String getName() { + return(protocolName); // return protocol name + } + + /** + Get the protocol peer. + + @return protocol peer + */ + public CSMAProtocol getPeer() { + return(protocolPeer); // return protocol peer + } + + /** + Return services currently offered. + + @return list of protocol services + */ + public Vector getServices() { + // System.err.println("CSMAProtocol.getServices - " + protocolName + + // ": protocolState <" + protocolState + "> protocolOut <" + protocolOut + + // "> protocolSending <" + protocolSending + ">"); + String peerName = protocolPeer.getName(); // get peer name + Vector list = new Vector(); // initialise protocol services + if (protocolState != NORMAL) { // handling collision? + if (protocolState == COLLISION || // collision detected or + protocolState == JAM_IN) { // jam received? + list.add(JAM + peerName); // append jam sending offer + } + else if (protocolState == BACKOFF) { // backing off? + // System.err.println("CSMAProtocol.getServices - " + protocolName + + // ": state local <" + protocolState + "> remote <" + + // protocolPeer.protocolState + ">, backoff local <" + + // protocolBackoff + "> remote <" + protocolPeer.protocolBackoff + + // ">, protocolRetries local <" + protocolRetries + "> remote <" + + // protocolPeer.protocolRetries + ">\n"); + int peerState = protocolPeer.protocolState; // get peer state + if (peerState == BACKOFF || // peer also backing off or + peerState == NORMAL) { // peer recovered from backoff? + if (protocolBackoff <= // local protocol can retry? + protocolPeer.protocolBackoff) { + protocolRetries++; // increment retry count + if (protocolRetries > CSMA.retryLimit) { // retry limit reached? + list.add(RETRIES); // add retry limit report + } + else { // retry limit not reached + protocolState = NORMAL; // set collision state to normal + protocolOut.type = CSMAPdu.START; // set PDU back to start + initialise_part(); // partly re-initialise protocol + list.add(START+ peerName + // append retry sending offer + " (retry " + protocolRetries + ")"); + } + } + else { // local entity must wait + initialise_part(); // partly re-initialise protocol + protocolDeferring = true; // note deferring + } + } + } + } // not handling collision hereon + else if (protocolOut != null) { // outgoing PDU available? + if (protocolSending) { // already sending? + if (protocolMedium.isEmpty(protocolName)) { // no local PDUs sent? + list.add(FINISH + peerName); // append finish sending offer + } + } + else if ((protocolMedium.isEmpty() || // nothing in medium or + CSMA.retryLimit > 0) && // retries allowed, and + protocolPeer.protocolState == // peer not in collision and + NORMAL && + !protocolDeferring) { // not deferring? + list.add(START + peerName); // append start sending offer + } + } + if (protocolIn != null && // incoming PDU available and + protocolState == NORMAL) { // not in collision? + String serviceName = protocolUser.getName(); // get service name + list.add(DELIVER + serviceName); // append deliver offer + } + return (list); // return protocol service list + } + + /** + Initialise the protocol entity. + */ + public void initialise() { + initialise_part(); // partly initialise protocol + protocolIn = null; // initialise incoming PDU + protocolOut = null; // initialise outgoing PDu + protocolRetries = 0; // initialise no retries + CSMA.randomIndex = 0; // initialise random index + } + + /** + Partly initialise the protocol entity in the event of backoff and retry. + */ + public void initialise_part() { + protocolDeferring = false; // initialise not deferring + protocolEvents = new Vector();// initialise protocol events + protocolReceiving = false; // initialise not receiving + protocolSending = false; // initialise not sending + protocolState = NORMAL; // initialise no collision + } + + /** + Perform service. + + @param service service request + @return resulting protocol events + */ + public Vector performService(String service) { + protocolEvents = new Vector(); // initialise protocol events + if (service.startsWith(DELIVER)) { // deliver request? + CSMASdu sdu = // create SDU from PDU + new CSMASdu(CSMASdu.DATA, protocolIn.sdu); + transmitPDU(sdu, protocolUser); // deliver SDU to service user + protocolIn = null; // re-initialise incoming PDU + } + else if (service.startsWith(FINISH)) { // finish sending request? + String message = protocolOut.sdu; // get current SDU + protocolOut = // create finish PDU + new CSMAPdu(CSMAPdu.FINISH, message); + protocolSending = false; // note not sending + protocolEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.TRANSMIT, protocolOut)); + transmitPDU(protocolOut, protocolPeer); // send protocol PDU + if (protocolPeer.protocolState == NORMAL) // peer is in normal state? + protocolOut = null; // re-initialise outgoing PDU + } + else if (service.startsWith(JAM)) { // jamming request? + if (protocolState == JAM_IN) { // jam received? + protocolState = BACKOFF; // note backing off + protocolBackoff = backoffCount(); // compute backoff "time" + protocolEvents.addElement( // add backoff event + comment("backoff " + protocolBackoff)); + } + else // jam not received + protocolState = JAM_OUT; // note jam sent + CSMAPdu pdu = new CSMAPdu(CSMAPdu.JAM, ""); // set jam PDU + protocolEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + transmitPDU(pdu, protocolPeer); // send protocol PDU + } + else if (service.startsWith(RETRIES)) { // retries response? + initialise(); // re-initialise whole protocol + CSMASdu sdu = // construct failure SDU + new CSMASdu(CSMASdu.FAIL, "Retry Limit"); + transmitPDU(sdu, protocolUser); // deliver SDU to service user + } + else if (service.startsWith(START)) { // start sending request? + String message = protocolOut.sdu; // get current SDU + protocolOut = // create start PDU + new CSMAPdu(CSMAPdu.START, message); + protocolSending = true; // note sending + if (protocolReceiving) { // already receiving? + protocolState = COLLISION; // note collision + protocolEvents.addElement( // add collision event + comment("collision")); + } + protocolEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.TRANSMIT, protocolOut)); + transmitPDU(protocolOut, protocolPeer); // send protocol PDU + } + return(protocolEvents); // return protocol events + } + + /** + Receive PDU. + + @param pdu PDU + @return resulting protocol events + */ + public Vector receivePDU(PDU pdu) { + protocolEvents = new Vector(); // initialise event list + if (pdu != null) { // non-empty PDU? + String type = pdu.type; // get PDU type + String data = pdu.sdu; // get SDU + if (type.equals(CSMASdu.DATA)) { // service user message? + protocolOut = // store outgoing PDU + new CSMAPdu(CSMAPdu.START, data); + if (protocolReceiving) { // already receiving + protocolDeferring = true; // note deferring + protocolEvents.addElement( // add defer event + comment("defer")); + } + } + else if (type.equals(CSMAPdu.FINISH)) { // finish receiving message? + protocolReceiving = false; // note not receiving + if (protocolState == NORMAL) // normal state? + protocolIn = (CSMAPdu) pdu; // store incoming PDU + if (protocolDeferring) { // deferring? + protocolDeferring = false; // not deferring + protocolEvents.addElement( // add resume event + comment("resume")); + } + } + else if (type.equals(CSMAPdu.JAM)) { // jam message? + if (protocolState == JAM_OUT) { // jam sent? + protocolState = BACKOFF; // note backing off + protocolBackoff = backoffCount(); // compute backoff "time" + protocolEvents.addElement( // add backoff event + comment("backoff " + protocolBackoff)); + } + else { // jam not sent + if (protocolState == NORMAL) { // normal state? + protocolEvents.addElement( // add collision event + comment("collision")); + } + protocolState = JAM_IN; // note jam received + } + } + else if (type.equals(CSMAPdu.START)) { // start receiving message? + protocolReceiving = true; // note receiving + if (protocolSending) { // already sending? + protocolState = COLLISION; // note collision + protocolEvents.addElement( // add collision event + comment("collision")); + } + else if (protocolOut != null) { // PDU to be sent? + protocolDeferring = true; // note deferring + if (protocolPeer.protocolState == NORMAL) // peer in normal state? + protocolEvents.addElement( // add defer event + comment("defer")); + } + if (protocolState == NORMAL && // normal state and + protocolDeferring) { // deferring to peer? + protocolPeer.protocolRetries = 0; // reset its retry count + } + } + } + return(protocolEvents); // return protocol events + } + + /** + Set the protocol peer. + + @param protocolPeer protocol peer + */ + public void setPeer(ProtocolEntity protocolPeer) { + this.protocolPeer = // set protocol peer + (CSMAProtocol) protocolPeer; + } + + /** + Set the protocol service. + + @param protocolUser protocol service user + */ + public void setUser(ProtocolEntity protocolUser) { + this.protocolUser = // set service user + (CSMAService) protocolUser; + } + + /** + Send PDU. + + @param pdu PDU + @param destination protocol destination + */ + public void transmitPDU(PDU pdu, ProtocolEntity destination) { + pdu.setSource(this); // set protocol entity as source + pdu.setDestination(destination); // set message destination + Vector receiveEvents; // declare receive events + if (destination == protocolPeer) { // for protocol peer? + receiveEvents = // receive PDU at medium + protocolMedium.receivePDU(pdu); + } + else // for service user? + receiveEvents = // receive PDU at user + protocolUser.receivePDU(pdu); + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + protocolEvents.addElement( // append receive event + receiveEvents.get(i)); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/CSMASdu.java b/lab8_protocols/jasper/source/protocol/CSMASdu.java new file mode 100755 index 0000000..c84dd2b --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMASdu.java @@ -0,0 +1,51 @@ +// CSMASdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines (de)multiplexer service messages. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010, KJT): initial version +*/ + +public class CSMASdu extends PDU { + + /** Service SDU type */ + public final static String DATA = "DATA"; + + /** Service retry limit failure */ + public final static String FAIL = "FAIL"; + + /** + Constructor for a source/sink service message. + + @param type SDU type + @param sdu SDU data + */ + public CSMASdu(String type, String sdu) { + super(type); // construct with type + this.sdu = sdu; // set SDU data + } + + /** + Return the label for an arrow representing a (de)multiplexer SDU in a time + sequence diagram. + + @return the label name + */ + public String getLabel() { + return(type + "(" + sdu + ")"); + } + + /** + Convert SDU to string. + */ + public String toString() { + return("SDU "); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/CSMAService.java b/lab8_protocols/jasper/source/protocol/CSMAService.java new file mode 100755 index 0000000..7bf06eb --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/CSMAService.java @@ -0,0 +1,179 @@ +// CSMAService.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports sources/sinks for (de)multiplexing. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010, KJT): initial version +*/ + +public class CSMAService implements ProtocolEntity { + + /** Service send offer */ + private final static String SEND = "Send data to "; + + /** Service message count */ + private static int messageCount; + + /** PDU sent by service */ + private PDU sduSent; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private CSMAProtocol serviceProvider; + + /** Service provider events */ + private Vector serviceEvents; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public CSMAService(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return a protocol event with the given comment. + + @param comment protocol comment + @return protocol event + */ + public ProtocolEvent comment(String comment) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, comment)); + } + + /** + Return a protocol event with an empty comment as a gap. + + @return protocol event + */ + public ProtocolEvent gap() { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, "")); + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + CSMAProtocol remoteProvider = // get remote service provider + serviceProvider.protocolPeer; + CSMAMedium protocolMedium = // get protocol medium + serviceProvider.protocolMedium; + String providerName = // get provider name + serviceProvider.protocolName; + // System.err.println("CSMAService.getServices - " + serviceName + + // ": in <" + serviceProvider.protocolIn + "> out <" + + // serviceProvider.protocolOut + ">"); + boolean localIdle = // check if local protocol idle + serviceProvider.protocolIn == null && // no incoming data pending? + serviceProvider.protocolOut == null && // no outgoing data pending? + !serviceProvider.protocolReceiving && // not receiving data? + protocolMedium.isEmpty(providerName); // no outgoing PDUs in transit? + boolean remoteIdle = // check if remote protocol idle + remoteProvider.protocolIn == null && // no incoming data pending and + remoteProvider.protocolState == CSMAProtocol.NORMAL; // no collision? + // System.err.println("CSMAService.getServices - localIdle <" + + // localIdle + "> remoteIdle <" + remoteIdle + ">\n"); + if (localIdle && remoteIdle) { // local and remote idle? + String name = // get peer system name + serviceProvider.protocolPeer.protocolUser.getName(); + list.addElement(SEND + name); // add send to list + } + return(list); // return service list + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.startsWith(SEND)) { // send request? + String message = "D" + messageCount; // create data message + PDU sdu = // create SDU + new CSMASdu(CSMASdu.DATA, message); + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + serviceEvents.addElement( // deliver SDU + new ProtocolEvent(ProtocolEvent.DELIVER, sdu)); + serviceEvents.addElement(gap()); // add a vertical gap + + return(serviceEvents); // return service events + } + + /** + Set the local service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = // set local service provider + (CSMAProtocol) serviceProvider; + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + serviceEvents.addElement( // append receive event + receiveEvents.get(i)); + sduSent = sdu; // stored the sent SDU + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/HTTP.java b/lab8_protocols/jasper/source/protocol/HTTP.java new file mode 100755 index 0000000..51c7135 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/HTTP.java @@ -0,0 +1,34 @@ +// HTTPSender.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of HTTP. + + @author Kenneth J. Turner, Kenneth A. Whyte + @version 1.4 (9th March 2006, KJT/KAW): initial version +
1.5 (19th July 2010, KJT): minor tidying +*/ + +public class HTTP extends Protocol { + + private HTTPSender sender; + private HTTPReceiver receiver; + + public HTTP() { + medium = new Medium(); + sender = new HTTPSender(medium, "Client"); + receiver = new HTTPReceiver(medium, "Server"); + sender.setPeer(receiver); + receiver.setPeer(sender); + entities = new Vector(); + entities.addElement(sender); + entities.addElement(medium); + entities.addElement(receiver); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/HTTPReceiver.java b/lab8_protocols/jasper/source/protocol/HTTPReceiver.java new file mode 100755 index 0000000..745b5e0 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/HTTPReceiver.java @@ -0,0 +1,139 @@ +// HTTPReceiver.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of an HTTP receiver (i.e. server). + + @author Kenneth J. Turner, Kenneth A. Whyte + @version 1.4 (9th March 2006, KJT/KAW): initial version +
1.5 (24th July 2010, KJT): minor tidying +*/ + +public class HTTPReceiver implements ProtocolEntity { + + private PDU pduReceived; + private PDU pduSent; + private ProtocolEntity peer; + private Medium medium; + private String name; + + private int index; // data/URL index for move + + public HTTPReceiver(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public void initialise() { + pduReceived = null; + index = 1; + } + + public Vector getServices() { + int url, moved; // given/moved URL number + Vector list = new Vector(); + String sdu; // SDU + + if (pduReceived != null && // PDU not null? + (sdu = pduReceived.sdu) != null && // SDU not null? + sdu.startsWith("URL")) { // URL and number? + try { // parse number + url = Integer.parseInt(sdu.substring(3)); // get URL number + } + catch (NumberFormatException exc) { // incorrect number + url = 0; // set default URL number + } + } + else // not URL + url = 0; // set zero URL number + if (pduReceived != null && pduReceived.type.equals("GET")) { + if (url == index) // URL same as local one? + moved = index + 1; // use next local for move + else // URL differs from local + moved = index; // use local for move + list.addElement("200 OK(DATA" + url + ") - send requested data"); + list.addElement("301 MOVED(URL" + moved + ") - report moved URL"); + list.addElement("400 ERROR(CODE) - report requested data unavailable"); + } + if (pduReceived != null && pduReceived.type.equals("HEAD")) { + if (url == index) // URL same as local one? + moved = index + 1; // use next local for move + else // URL differs from local + moved = index; // use local for move + list.addElement + ("200 OK(HEADER" + url + ") - send requested header"); + list.addElement + ("301 MOVED(URL" + moved + ") - report moved URL"); + list.addElement + ("400 ERROR(CODE) - report requested header unavailable"); + } + if (pduReceived != null && pduReceived.type.equals("POST")) { + list.addElement("200 OK - acknowledge receipt of data"); + list.addElement("400 ERROR(CODE) - report data not posted"); + } + if (pduReceived != null && pduReceived.type.equals("PUT")) { + list.addElement("200 OK - acknowledge receipt of data"); + list.addElement("400 ERROR(CODE) - report data not posted"); + } + return(list); + } + + public Vector performService(String s) { + Vector events = new Vector(); + pduSent = null; + String data, url; + int startIndex, endIndex; + + startIndex = s.indexOf ('(') + 1; + endIndex = s.indexOf (')'); + if (s.startsWith ("200")) { + if (startIndex < endIndex) // any data? + data = s.substring(startIndex, endIndex); + else // no data + data = ""; + transmitPDU(new PDU("200 OK", data), peer); + } + if (s.startsWith ("301")) { + url = s.substring(startIndex, endIndex); // get URL + index++; // update local index + transmitPDU(new PDU("301 MOVED", url), peer); + } + if (s.startsWith("400")) { + data = s.substring(startIndex, endIndex); // get data + transmitPDU(new PDU("400 ERROR", data), peer); + } + if (pduSent != null) { + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + events.addElement(new ProtocolEvent(ProtocolEvent.RECEIVE, pduSent)); + } + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + pduSent = pdu; + peer.receivePDU(pdu); + pduReceived = null; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/HTTPSender.java b/lab8_protocols/jasper/source/protocol/HTTPSender.java new file mode 100755 index 0000000..d9302a5 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/HTTPSender.java @@ -0,0 +1,155 @@ +// HTTPSender.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of an HTTP sender (i.e. client). + + @author Kenneth J. Turner, Kenneth A. Whyte + @version 1.4 (9th March 2006, KJT/KAW): initial version +
1.5 (19th July 2010, KJT): minor tidying +*/ + +public class HTTPSender implements ProtocolEntity { + + // Protocol state constants + private static final int IDLE = 0; // ready to send + private static final int GET_SENT = 1; // waiting GET resp. + private static final int HEAD_SENT = 2; // waiting HEAD resp. + private static final int POST_SENT = 3; // waiting POST resp. + private static final int PUT_SENT = 4; // waiting PUT resp. + + private int state; + + private PDU pduSent; + private PDU pduReceived; + private ProtocolEntity peer; + private Medium medium; + private String name; + + private int index; // data/URL index + + public HTTPSender(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + String pduType, pduData; + + if (pduReceived != null) { + pduType = pduReceived.type; + pduData = pduReceived.sdu; + } + else { + pduType = ""; + pduData = ""; + } + switch (state) { + case GET_SENT: + if (pduType.startsWith("200") || pduType.startsWith("400")) + state = IDLE; + else if (pduType.startsWith("301")) + list.addElement(pduSent.type + "(" + pduData + ") - get data for new URL"); + break; + case HEAD_SENT: + if (pduType.startsWith("200") || pduType.startsWith("400")) + state = IDLE; + else if (pduType.startsWith("301")) + list.addElement(pduSent.type + "(" + pduData + ") - get header for new URL"); + break; + case POST_SENT: + case PUT_SENT: + if (pduType.startsWith("200") || pduType.startsWith("400")) + state = IDLE; + break; + } + if (state == IDLE) { + list.addElement("GET(URL" + index + ") - get data for URL"); + list.addElement("HEAD(URL" + index + ") - get header for URL"); + list.addElement("POST(URL" + index + ",DATA" + + index + ") - append data to URL"); + list.addElement("PUT(URL" + index + ",DATA" + + index + ") - send data to URL"); + } + return(list); + } + + public void initialise() { + state = IDLE; + index = 1; + pduSent = null; + pduReceived = null; + } + + public Vector performService (String s) { + Vector events = new Vector(); + int startIndex, midIndex, endIndex; // start/mid/end subscripts + String url = ""; // current URL + String data; // URL data + + pduSent = null; + startIndex = s.indexOf ('(') + 1; // get URL start + endIndex = s.indexOf (')'); // get URL end + if (s.startsWith ("GET")) { // GET? + url = s.substring(startIndex, endIndex); // get URL + transmitPDU(new PDU("GET", url), peer); + state = GET_SENT; // GET now sent + } + else if (s.startsWith ("HEAD")) { // HEAD? + url = s.substring(startIndex, endIndex); // get URL + transmitPDU(new PDU("HEAD", url), peer); + state = HEAD_SENT; // HEAD now sent + } + else if (s.startsWith ("POST")) { // POST? + midIndex = s.indexOf (','); // get URL end + url = s.substring(startIndex, midIndex); // get URL + data = s.substring(midIndex + 1, endIndex); // get URL data + transmitPDU(new PDU("POST", url + "," + data), peer); + state = POST_SENT; // POST now sent + } + else if (s.startsWith ("PUT")) { // PUT? + midIndex = s.indexOf (','); // get URL end + url = s.substring(startIndex, midIndex); // get URL + data = s.substring(midIndex + 1, endIndex); // get URL data + transmitPDU(new PDU("PUT", url + "," + data), peer); + state = PUT_SENT; // PUT now sent + } + if (pduSent != null) { + startIndex = url.indexOf ("URL"); // get URL start + index = // extract/increment index + Integer.parseInt(url.substring(startIndex + 3)) + 1; + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + events.addElement( new ProtocolEvent(ProtocolEvent.RECEIVE, pduSent)); + } + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + pduSent = pdu; + peer.receivePDU(pdu); + pduReceived = null; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/IP.java b/lab8_protocols/jasper/source/protocol/IP.java new file mode 100755 index 0000000..1245f9e --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/IP.java @@ -0,0 +1,72 @@ +// IP.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class IP extends Protocol { + + private IPService userA; + private IPService userB; + private IPProtocol protA; + private IPProtocol protB; + + public IP() { + medium = new IPMedium(); + userA = new IPService("User A"); + userB = new IPService("User B"); + protA = new IPProtocol(medium, "Protocol A"); + protB = new IPProtocol(medium, "Protocol B"); + userA.setProvider(protA); + userB.setProvider(protB); + protA.setUser(userA); + protA.setPeer(protB); + protB.setUser(userB); + protB.setPeer(protA); + entities = new Vector(); + entities.addElement(userA); + entities.addElement(protA); + entities.addElement(medium); + entities.addElement(protB); + entities.addElement(userB); + } + + public void setParameter(String param, String value) { + IPMedium ipMedium = (IPMedium)medium; + if (param.equals("misordering")) { + ipMedium.setMisordering(Boolean.valueOf(value).booleanValue()); + System.out.println(param + " set: " + value); + return; + } + try { + if (param.equals("lossRate")) { + ipMedium.setLossRate(Float.valueOf(value).floatValue()); + System.out.println(param + " set: " + value); + return; + } + int size = Integer.parseInt(value); + if (param.equals("userMessageSize")) { + userA.setMessageSize(size); + userB.setMessageSize(size); + System.out.println(param + " set: " + size); + return; + } + if (param.equals("maxProtocolMessageSize")) { + protA.setMaxMessageSize(size); + protB.setMaxMessageSize(size); + System.out.println(param + " set: " + size); + return; + } + if (param.equals("maxMediumMessageSize")) { + ipMedium.setMaxMessageSize(size); + System.out.println(param + " set: " + size); + return; + } + } + catch (NumberFormatException e) { // invalid number value + System.out.println(param + " number format exception: " + value); + } + } + +} diff --git a/lab8_protocols/jasper/source/protocol/IPMedium.java b/lab8_protocols/jasper/source/protocol/IPMedium.java new file mode 100755 index 0000000..a989855 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/IPMedium.java @@ -0,0 +1,195 @@ +// IPMedium.java + +package protocol; + +import java.util.*; +import support.*; + +/** + This is the class for an IP medium. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (24th July 2010, KJT): minor tidying +*/ +public class IPMedium extends Medium { + + private int maxMessageSize = 100; // maximum message size + private boolean misordering = true; // misordering allowed? + private float lossRate = 0.2f; // message loss rate + private Vector randomNumbers; // random numbers + private int randomIndex; // random number index + + public IPMedium() { + super(); // construct superclass + randomNumbers = new Vector(); // initialise random numbers + } + + public void initialise() { + super.initialise(); + randomIndex = 0; // initialise random index + } + + public void setMaxMessageSize(int size) { + maxMessageSize = size; + } + + public void setMisordering(boolean b) { + misordering = b; + } + + public void setLossRate(float rate) { + lossRate = rate; + } + + // randomise the order of the elements of vector v + + private void misorder(Vector v) { + Vector t = new Vector(v); + int size = v.size(); + v.removeAllElements(); + while (size > 0) { + // choose an element of t at random and add it to v + int index = Math.round(--size * random()); + v.addElement(t.elementAt(index)); + t.removeElementAt(index); + } + } + + // Identify and return PDU currently on channel with + // type and parameters matching those described in given string + // Parameters are: + // ( messageID [,fragment offset] [,D] [,M] ) + // where D and M are booleans + // "D" indicates Don't Fragment + // "M" indicates More Fragments Follow + + protected PDU getMatchingPDU(String s) { + IPMessage ipd; + int messageID = -1; + int offset = -1; + // extract name of source entity for this datagram: + int sourceStart = s.indexOf('[') + 1; + int sourceEnd = s.indexOf( ']' ); + String sourceName = s.substring(sourceStart, sourceEnd); + // get type and parameters of PDU from action string + // PDU type starts after first space following "Deliver" or "Lose": + int typeStart = s.indexOf(' ') + 1; + int typeEnd = s.indexOf( '(' ); + if (typeEnd < 0) typeEnd = sourceStart - 2; + String type = s.substring(typeStart, typeEnd); + String[] params = getParams(s); + messageID = Integer.parseInt(params[0]); + if (params.length > 1) { // there is a fragment offset parameter + try { + offset = Integer.parseInt(params[1]); // fragment offset + } + catch (NumberFormatException e) { // not offset parameter - ignore + } + } + // try to match IP datagram type and messageID, and fragment offset where + // applicable, with a datagram currently on channel: + for (Enumeration e = pdus.elements(); e.hasMoreElements(); ) { + ipd = (IPMessage)e.nextElement(); + if (ipd != null && ipd.type.equals(type) + && ipd.getSource().getName().equals(sourceName) + && messageID == ipd.messageID + && (offset < 0 || (offset >= 0 && offset == ipd.fragOffset))) + return(ipd); + } + return(null); // no match found + } + + /** Return a list of strings describing actions (services) that can be carried + out in the current state */ + + public Vector getServices() { + Vector list = new Vector(); + for (Enumeration e = pdus.elements(); e.hasMoreElements(); ) { + PDU pdu = (PDU)e.nextElement(); + if (pdu != null) { + String s = pdu.getID(); + s += " [" + pdu.getSource().getName() + "]"; + if (misordering) + list.addElement("Deliver " + s + " - fragmentation/misordering"); + else + list.addElement("Deliver " + s + " - fragmentation"); + list.addElement("Lose " + s + " - congestion/error"); + } + } + return(list); + } + + /** Carry out the action specified in the given string, and return a list of + protocol events resulting from this action */ + + public Vector performService(String s) { + Vector events = new Vector(); + IPMessage frag; + IPMessage ipd = (IPMessage)getMatchingPDU(s); + if (ipd == null) return(events); + if (s.startsWith("Deliver")) { + ProtocolEntity destination = ipd.getDestination(); + // Split message into fragments (up to size maxMessageSize) + int messageSize = ipd.size; + for (int relOffset = 0; relOffset < messageSize; + relOffset += maxMessageSize) { + int size = Math.min(messageSize - relOffset, maxMessageSize); + int offset = ipd.fragOffset + relOffset; + frag = new IPMessage("DT", ipd.messageID, offset, size); + frag.setSDU(ipd.getSDU()); + frag.setMoreFrags(ipd.hasMoreFrags() || + relOffset + maxMessageSize < messageSize); + frag.setSource(ipd.getSource()); + frag.setDestination(destination); + // decide randomly whether to lose fragment + if (random() >= lossRate) + events.addElement(new ProtocolEvent(ProtocolEvent.FRAGMENT, frag)); + else + events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, frag)); + } + if (misordering) + misorder(events); // transmit fragments in random order + for (Enumeration e = events.elements(); e.hasMoreElements(); ) { + ProtocolEvent event = (ProtocolEvent) e.nextElement(); + if (event.getType() == ProtocolEvent.FRAGMENT) { + IPMessage ipdSent = (IPMessage)event.getPDU(); + transmitPDU(ipdSent, destination); + } + } + pdus.removeElement(ipd); + } + if (s.startsWith("Lose")) { + destinationEvents.removeAllElements(); + pdus.removeElement(ipd); + events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, ipd)); + } + // tack on any events triggered by delivery of a message + // to its destination protocol entity + for (Enumeration e = destinationEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + + /** + Return a random float. If the list of randoms contains an unused value then + use this, otherwise generate a fresh value and add to the list. This allows + random numbers to be generated consistently when simulation steps are + undone/redone. + + @return random number in the range (0..1] + */ + private float random() { + if (randomIndex < randomNumbers.size()) // index within list? + return( // return this random + ((Float)randomNumbers.elementAt(randomIndex++)).floatValue()); + else { // index not within list + Float random = new Float(Math.random()); // get random number + randomNumbers.addElement(random); // add random to list + randomIndex++; // increment list index + return(random.floatValue()); // return rand number + } + } + +} diff --git a/lab8_protocols/jasper/source/protocol/IPMessage.java b/lab8_protocols/jasper/source/protocol/IPMessage.java new file mode 100755 index 0000000..fd421a0 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/IPMessage.java @@ -0,0 +1,77 @@ +// IPMessage.java (C) I. A. Robin, K. J. Turner 01/03/01 + +package protocol; + +import support.*; + +public class IPMessage extends PDU { + + int messageID; + int fragOffset = -1; // fragment offset + private boolean dontFrag = false; + private boolean moreFrags = false; + + public IPMessage(String type, int mid, int length) { + super(type); + messageID = mid; + this.size = length; + } + + public IPMessage(String type, int mid, int offset, int length) { + super(type); + messageID = mid; + fragOffset = offset; + this.size = length; + } + + public void setDontFrag(boolean d) { + dontFrag = d; + } + + public boolean dontFrag() { + return dontFrag; + } + + public void setMoreFrags(boolean m) { + moreFrags = m; + } + + public boolean hasMoreFrags() { + return moreFrags; + } + + public boolean matches(PDU pdu) { + if (pdu == null || pdu.getClass() != IPMessage.class) + return false; + IPMessage ipd = (IPMessage)pdu; + if (type.equals(ipd.type) && source == ipd.getSource() + && messageID == ipd.messageID + && (fragOffset < 0 || + fragOffset >= 0 && + ipd.fragOffset >= fragOffset && + ipd.fragOffset < fragOffset + size)) return true; + return false; + } + + // return label for arrow representing this ip datagram + // in a time sequence diagram + + public String getLabel() { + String label = getID(); + if (fragOffset >= 0) label += moreFrags? " +" : " -"; + return label; + } + + // return string representing this pdu in actions menu + + public String getID() { + String id = type + "(" + messageID; + if (fragOffset >= 0) { + id += "," + fragOffset; + if (dontFrag) id += ",D"; + } + id += "," + size + ")"; + return id; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/IPProtocol.java b/lab8_protocols/jasper/source/protocol/IPProtocol.java new file mode 100755 index 0000000..460432b --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/IPProtocol.java @@ -0,0 +1,186 @@ +// IPProtocol.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class IPProtocol implements ProtocolEntity { + + private static final String TTL_EXPIRY = "Time-To-Live expiry: Message ID "; + + private ProtocolEntity peer; + private IPService user; + private Vector userEvents; + private Medium medium; + private String name; + private int maxMessageSize = 200; + private Vector expiredMIDs; // expired message IDs + private Vector recvFragments; // message fragments + private int messageID; // current message ID + + public IPProtocol(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public void initialise() { + messageID = 0; + expiredMIDs = new Vector(); + userEvents = new Vector(); + recvFragments = new Vector(); + } + + public String getName() { + return(name); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void setUser(ProtocolEntity user) { + this.user = (IPService)user; + } + + public void setMaxMessageSize(int maxSize) { + maxMessageSize = maxSize; + } + + // if all fragments of message with message identifier mID + // have arrived, return total message size, else return 0 + + private int completeMessageSize(int mID) { + int expectedSize = 0; + int sizeSoFar = 0; + for (Enumeration e = recvFragments.elements(); e.hasMoreElements(); ) { + IPMessage ipd = (IPMessage)e.nextElement(); + if (ipd.messageID == mID) { + sizeSoFar += ipd.size; + if (!ipd.hasMoreFrags()) { + // last fragment has "More Fragments" flag unset; get + // message size from offset and size of last fragment + expectedSize = ipd.fragOffset + ipd.size; + } + } + } + if (expectedSize > 0 && sizeSoFar == expectedSize) return(expectedSize); + return(0); // message incomplete + } + + private void removeFragments(int mID) { + // remove all message fragments with message identifier mID + Enumeration e = recvFragments.elements(); + while (e.hasMoreElements()) { + IPMessage ipd = (IPMessage)e.nextElement(); + if (ipd.messageID == mID) { + recvFragments.removeElement(ipd); + e = recvFragments.elements(); + } + } + } + + // add new message ID to expired list + // but not if this message ID already appears in list + + private void addExpired(int mID) { + Integer iMID = new Integer(mID); + if (expiredMIDs.indexOf(iMID) < 0) + expiredMIDs.addElement(iMID); + } + + // return true if TTL for message ID has expired + + private boolean expired(int mID) { + Integer iMID = new Integer(mID); + return(expiredMIDs.indexOf(iMID) >= 0); + } + + // find oldest (ie smallest) message identifier of + // any message fragment received so far + // return -1 if no messages have been received yet + + private int oldestMessageID() { + if (recvFragments.isEmpty()) return(-1); + IPMessage ipd = (IPMessage)recvFragments.firstElement(); + int oldestSoFar = ipd.messageID; + for (Enumeration e = recvFragments.elements(); e.hasMoreElements(); ) { + ipd = (IPMessage)e.nextElement(); + oldestSoFar = Math.min(oldestSoFar, ipd.messageID); + } + return(oldestSoFar); + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + String pduType = ""; + int size; + if (pdu != null) { + pduType = pdu.type; + if (pduType.equals("DatReq")) { // DatReq from user + int messageSize = pdu.size; + // fragment message + for (int offset = 0; offset < messageSize; offset += maxMessageSize) { + size = Math.min(messageSize - offset, maxMessageSize); + IPMessage fragment = new IPMessage("DT", messageID, offset, size); + fragment.setSDU(pdu.getSDU()); + fragment.setMoreFrags(offset + maxMessageSize < messageSize); + transmitPDU(fragment, peer); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, fragment)); + } + messageID++; + } + if (pduType.equals("DT")) { // data PDU received + // simulate reconstruction of message fragments + IPMessage ipd = (IPMessage)pdu; + int mID = ipd.messageID; + if (!expired(mID)) recvFragments.addElement(ipd); + size = completeMessageSize(mID); + if (size > 0) { // all fragments rcvd. + PDU pduSent = new PDU("DatInd", pdu.getSDU()); + transmitPDU(pduSent, user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + // dispose of fragments of delivered message: + removeFragments(mID); + } + } + } + return(events); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + if (dest == peer) + userEvents = medium.receivePDU(pdu); + else + userEvents = user.receivePDU(pdu); + } + + public Vector getServices() { + Vector services = new Vector(); + int oldestMID = oldestMessageID(); + if (oldestMID >= 0) + services.addElement(TTL_EXPIRY + oldestMID); + return(services); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith(TTL_EXPIRY)) { + int mIDIndex = s.indexOf("ID") + 3; + int mID = Integer.parseInt(s.substring(mIDIndex)); + removeFragments(mID); + addExpired(mID); + events.addElement( + new ProtocolEvent(ProtocolEvent.COMMENT, this, + "TTL expiry " + mID)); + } + for (Enumeration e = userEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/IPService.java b/lab8_protocols/jasper/source/protocol/IPService.java new file mode 100755 index 0000000..8bd0818 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/IPService.java @@ -0,0 +1,71 @@ +// IPService.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class IPService implements ProtocolEntity { + + public static final String DAT_REQ = "Data Request"; + + private ProtocolEntity provider; // protocol for service + private Vector providerEvents; + private String name; + private int block; // block seq. no. + private int messageSize = 400; + + public IPService(String name) { + this.name = name; + initialise(); + } + + public void initialise() { + block = 0; + providerEvents = new Vector(); + } + + public String getName() { + return(name); + } + + public void setProvider(ProtocolEntity provider) { + this.provider = provider; + } + + public void setMessageSize(int size) { + messageSize = size; + } + + public Vector receivePDU(PDU pdu) { + return(new Vector()); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + providerEvents = dest.receivePDU(pdu); + } + + public Vector getServices() { + Vector list = new Vector(); + list.addElement("Send " + DAT_REQ + "(D" + block + ")"); + return(list); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith("Send " + DAT_REQ)) { + String sdu = "D" + block; + PDU pdu = new PDU("DatReq", sdu); + pdu.size = messageSize; + transmitPDU(pdu, provider); + block++; + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + } + for (Enumeration e = providerEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/MCAST.java b/lab8_protocols/jasper/source/protocol/MCAST.java new file mode 100755 index 0000000..88b3737 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MCAST.java @@ -0,0 +1,132 @@ +// MCAST.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of multicasting. It handles broadcast, + unicast and multicast. + + @author Kenneth J. Turner + @version 1.0 (22nd July 2010, KJT): initial version +*/ +public class MCAST extends Protocol { + + /** Broadcast service mode */ + public final static int BROADCAST = 0; + + /** Multiple unicast service mode */ + public final static int UNICAST = 1; + + /** Multicast service mode */ + public final static int MULTICAST = 2; + + /** Service entities */ + private static Vector serviceEntities; + + /** Service mode */ + public static int serviceMode = BROADCAST; + + /** Source */ + private MCASTSource source; + + /** Router A */ + private MCASTRouter userA; + + /** Router B */ + private MCASTRouter userB; + + /** Router C */ + private MCASTRouter userC; + + /** Router D */ + private MCASTRouter userD; + + /** Router E */ + private MCASTRouter userE; + + /** Router F */ + private MCASTRouter userF; + + /** + Constructor for an MCAST object. + */ + public MCAST() { + source = new MCASTSource("Source"); // create source + userA = new MCASTRouter("Router A"); // create router A + userB = new MCASTRouter("Router B"); // create router B + userC = new MCASTRouter("Router C"); // create router C + userD = new MCASTRouter("Router D"); // create router D + userE = new MCASTRouter("Router E"); // create router E + userF = new MCASTRouter("Router F"); // create router F + + serviceEntities = new Vector(); + serviceEntities.addElement(source); // add source + serviceEntities.addElement(userA); // add router A + serviceEntities.addElement(userB); // add router B + serviceEntities.addElement(userC); // add router C + serviceEntities.addElement(userD); // add router D + serviceEntities.addElement(userE); // add router E + serviceEntities.addElement(userF); // add router F + entities = serviceEntities; // store entities for protocol + } + + /** + Return a protocol event with the given comment. + + @param entity protocol entity + @param comment protocol comment + @return protocol event + */ + public static ProtocolEvent comment(ProtocolEntity entity, String comment) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, entity, comment)); + } + + /** + Get an entity by letter. + + @param entityLetter entity letter ('A', etc.) + @return protocol entity + */ + public static ProtocolEntity getEntity(char entityLetter) { + int index = entityLetter - 'A' + 1; + return(serviceEntities.get(index)); // return entity for index + } + + /** + Get an entity's SDUs. + + @param entityLetter entity router letter ('A', etc.) + @return SDU held by router + */ + public static Vector getSDUs(char entityLetter) { + ProtocolEntity entity = // get entity for letter + getEntity(entityLetter); + return(((MCASTRouter) entity).getSDUs()); // return SDUs + } + + /** + Set a parameter of the MCAST simulation. + + @param parameter parameter name (broadcast, multicast, unicast) + @param value parameter value + */ + public void setParameter(String parameter, String value) { + if (parameter.equals("serviceMode")) { // service count? + if (value.equalsIgnoreCase("broadcast")) // broadcast? + serviceMode = BROADCAST; // note as broadcast + else if (value.equalsIgnoreCase("multicast")) // multicast? + serviceMode = MULTICAST; // note as multicast + else if (value.equalsIgnoreCase("unicast")) // unicast? + serviceMode = UNICAST; // note as unicast + else // not broad/multi/unicast + System.err.println( // report error + "MUX.setParameter: invalid service mode '" + value + "'"); + } + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MCASTRouter.java b/lab8_protocols/jasper/source/protocol/MCASTRouter.java new file mode 100755 index 0000000..8f720d6 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MCASTRouter.java @@ -0,0 +1,273 @@ +// MCASTRouterA.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports a router for broad/uni/multicast. + + @author Kenneth J. Turner + @version 1.0 (22nd July 2010, KJT): initial version +*/ + +public class MCASTRouter implements ProtocolEntity { + + /** Service send offer */ + private final static String SEND = "Forward message for router "; + + /** Current router letter */ + public char currentRouter; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit output */ + private Vector sdus; + + /** Service entity name */ + private String serviceName; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public MCASTRouter(String serviceName) { + this.serviceName = serviceName; // set service name + currentRouter = lastOf(serviceName); // set current router letter + initialise(); // initialise service entity + } + + /** + Find the SDU with the given destinations. + + @param destinations destinations + @return the corresponding SDU (or null if none) + */ + public MCASTSdu findSDU(String destinations) { + MCASTSdu result = null; // initialise SDU result + for (int i = 0; i < sdus.size(); i++) { // go through SDUs + MCASTSdu sdu = sdus.get(i); // get SDU + if (sdu.destinations.equals(destinations)) { // destinations found? + result = sdu; // save result + break; // exit loop + } + } + return(result); // return SDU + } + + /** + Get first character of string. + + @param string string to extract from + @return first character of string + */ + public char firstOf(String string) { + char letter = '?'; // initialise letter + if (string != null) { // non-null string? + int length = string.length(); // get string length + if (length > 0) // non-empty string? + letter = string.charAt(0); // get first character + } + return(letter); // return first character + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Return the service data units. + + @return service data units + */ + public Vector getSDUs() { + return(sdus); // return service data units + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (sdus != null && sdus.size() > 0) { // SDUs from service user? + for (int i = 0; i < sdus.size(); i++) { // go through SDUs + MCASTSdu sdu = sdus.get(i); // get SDU + String destinations = sdu.destinations; // get SDU destinations + String destination = ""; // initialise destination + for (int j = 0; j < destinations.length(); j++) { // go through dests. + if (j > 0) // not first destination? + destination += ", "; // append comma + destination += destinations.charAt(j);// append destination + } + char router = firstOf(destination); // get router letter + if (router != currentRouter) // not current router? + list.addElement(SEND + destination); // add send to list + } + } + return(list); // return service list + } + + /** + Initialise the service entity. + */ + public void initialise() { + serviceEvents = new Vector();// initialise service events + sdus = new Vector(); // initialise SDUs + } + + /** + Get last character of string. + + @param string string to extract from + @return last character of string + */ + public char lastOf(String string) { + char letter = '?'; // initialise letter + if (string != null) { // non-null string? + int length = string.length(); // get string length + if (length > 0) // non-empty string? + letter = string.charAt(length - 1); // get last character + } + return(letter); // return last character + } + + /** + Check if the destination router is more than one hop from the current router + (i.e. is not the next router letter. + + @param destination entity destination + @return true/false if destination is not/is adjacent + */ + public boolean laterRouter(ProtocolEntity destination) { + int currentIndex = (int) currentRouter; // get current router index + int destinationIndex = // get destination router index + (int) ((MCASTRouter) destination).currentRouter; + return(destinationIndex > currentIndex + 1);// return router letter check + } + + /** + Return the next router to reach the destination. This is a very restricted + routing table for the purposes of this example. + + @param destination entity destination + @return next entity destination + */ + public ProtocolEntity nextRouter(ProtocolEntity destination) { + char destinationLetter = // get destination router letter + ((MCASTRouter) destination).currentRouter; + char nextLetter = destinationLetter; // assume destination by default + switch (currentRouter) { // route based on current router + case 'A': // at router A? + switch (destinationLetter) { // check destination index + case 'D': // to router D? + nextLetter = 'C'; // via C + break; + case 'E': // to router E? + nextLetter = 'C'; // via C + break; + } + break; + } + return(MCAST.getEntity(nextLetter)); // return next router entity + } + + /** + Perform service. Note that the current implementation works for a particular + topology but is not general. When there are multiple destinations, the + router forward to the first two of these. A more general implementation + would embody particular knowledge about routing. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.startsWith(SEND)) { // send request? + String destinations = // get destinations + service.substring(SEND.length()); + String destination = // remove destination commas + destinations.replaceAll(", ", ""); // get destination(s) + MCASTSdu sdu = findSDU(destination); // find corresponding SDU + char router = firstOf(destination); // get destination router letter + serviceEvents.addElement( // add forward comment + MCAST.comment(this, "forward")); + transmitPDU(sdu, router); // transmit SDU + sdus.remove(sdu); // remove SDU from SDUs + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + String destinations = // get SDU destination(s) + ((MCASTSdu) sdu).destinations; + if (destinations.length() > 1) { // more than one destination? + String message = ((MCASTSdu) sdu).sdu; // get SDU message + sdu = // get SDU for first destination + new MCASTSdu(destinations.substring(0, 1), message); + sdus.addElement((MCASTSdu) sdu); // store SDU + sdu = // get SDU for later dests. + new MCASTSdu(destinations.substring(1), message); + sdus.addElement((MCASTSdu) sdu); // store SDU + } + else // one destination + sdus.addElement((MCASTSdu) sdu); // store SDU + } + return(serviceEvents); // return service events + } + + /** + Send an SDU to the given destination letter. + + @param sdu SDU + @param destination destination letter + */ + public void transmitPDU(PDU sdu, char destination) { + ProtocolEntity entityDestination = // get protocol entity dest. + MCAST.getEntity(destination); + transmitPDU(sdu, entityDestination); // transmit PDU + } + + /** + Send an SDU to the given destination entity. + + @param sdu SDU + @param destination destination entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + destination = nextRouter(destination); // set next router destination + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + if (laterRouter(destination)) { // later router? + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.TRAVERSE, sdu)); + } + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + serviceEvents.addElement( // append receive event + receiveEvents.get(i)); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MCASTSdu.java b/lab8_protocols/jasper/source/protocol/MCASTSdu.java new file mode 100755 index 0000000..09fdc12 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MCASTSdu.java @@ -0,0 +1,107 @@ +// MCASTSdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines SDUs for broad/uni/multicast. + + @author Kenneth J. Turner + @version 1.0 (22nd July 2010, KJT): initial version +*/ + +public class MCASTSdu extends PDU { + + /** Service SDU type */ + public final static String TYPE = "DT"; + + /** Service destinations (router letters) */ + public String destinations; + + /** Service message */ + public String sdu; + + /** + Constructor for a broad/uni/multicast service message. + + @param destination destination letter + @param sdu SDU data + */ + public MCASTSdu(char destination, String sdu) { + super(TYPE); // construct with type + this.destinations = destination + ""; // set SDU destinations + this.sdu = sdu; // set SDU data + } + + /** + Constructor for a broad/uni/multicast service message. + + @param destinations destinations + @param sdu SDU data + */ + public MCASTSdu(String destinations, String sdu) { + super(TYPE); // construct with type + this.destinations = destinations; // set SDU destinations + this.sdu = sdu; // set SDU data + } + + /** + Return the destinations. + + @return destinations (router letters) + */ + public String getDestinations() { + return(destinations); + } + + /** + Return the label for an arrow representing a protocol stack SDU in a time + sequence diagram. + + @return the label name + */ + public String getLabel() { + return type + "(" + destinations + "," + sdu + ")"; + } + + /** + Return the SDU. + + @return SDU + */ + public String getSDU() { + return(sdu); + } + + /** + Set the destinations. + + @param destinations destinations + */ + public void setDestinations(String destinations) { + this.destinations = destinations; + } + + /** + Set the SDU. + + @param sdu SDU + */ + public void setSDU(String sdu) { + this.sdu = sdu; + } + + /** + Convert SDU to string. + + @return SDU as string + */ + public String toString() { + return( + "SDU "); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MCASTSource.java b/lab8_protocols/jasper/source/protocol/MCASTSource.java new file mode 100755 index 0000000..02a5598 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MCASTSource.java @@ -0,0 +1,228 @@ +// MCASTSource.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the source for broad/uni/multicast. + + @author Kenneth J. Turner + @version 1.0 (24th July 2010, KJT): initial version +*/ +public class MCASTSource implements ProtocolEntity { + + /** Service broadcast send offer */ + private final static String SEND_BROADCAST = + "Broadcast message for routers B, D, E, F"; + + /** Service multicast send offer */ + private final static String SEND_MULTICAST = + "Multicast message for routers B, D, E"; + + /** Service unicast send offer */ + private final static String SEND_UNICAST = + "Unicast message for routers B, D, E"; + + /** Service message count */ + private int messageCount; + + /** Service data unit output */ + private PDU sdu; + + /** Service data unit destination letters */ + private String serviceDestinations; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service entity name */ + private String serviceName; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public MCASTSource(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Check if the service destinations have received an SDU. If so, re-initialise + these destinations. + + @return true/false if all service destinations have/have not + received an SDU + */ + public boolean checkDestinations() { + boolean result = false; // initialise result + int sduCount = 0; // initialise SDU count + int length = serviceDestinations.length(); // get number of destinations + for (int i = 0; i < length; i++) { // go through destinations + Vector sdus = // get SDUs for destination + MCAST.getSDUs(serviceDestinations.charAt(i)); + if (sdus != null && sdus.size() > 0) // destination has SDUs? + sduCount++; // increment SDU count + } + if (sduCount == length) { // all destinations have SDUs? + result = true; // note true result + for (int i = 0; i < length; i++) { // go through destinations + ProtocolEntity entity = // get destination entity + MCAST.getEntity(serviceDestinations.charAt(i)); + entity.initialise(); // re-initialise entity + } + } + return(result); // return check result + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (sdu == null || checkDestinations()) { // no SDU sent or all received? + if (MCAST.serviceMode == MCAST.BROADCAST) // broadcast? + list.addElement(SEND_BROADCAST); // add broadcast send to list + else if (MCAST.serviceMode == MCAST.MULTICAST) // multicast? + list.addElement(SEND_MULTICAST); // add multicast send to list + else if (MCAST.serviceMode == MCAST.UNICAST) // multicast? + list.addElement(SEND_UNICAST); // add unicast send to list + } + return(list); // return service list + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + sdu = null; // initialise SDU output + serviceEvents = new Vector();// initialise service events + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + String message = "M" + messageCount; // create message + messageCount++; // increment message count + if (service.equals(SEND_BROADCAST)) { // send broadcast request? + MCAST.serviceMode = MCAST.BROADCAST; // set broadcast mode + serviceDestinations = "BDEF"; // set service destinations + sdu = new MCASTSdu("B", message); // create SDU to B + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + sdu = new MCASTSdu("D", message); // create SDU to D + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + sdu = new MCASTSdu("E", message); // create SDU to E + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + sdu = new MCASTSdu("F", message); // create SDU to F + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + } + else if (service.equals(SEND_MULTICAST)) { // send multicast request? + MCAST.serviceMode = MCAST.MULTICAST; // set multicast mode + serviceDestinations = "BDE"; // set service destinations + sdu = new MCASTSdu("BDE", message); // create SDU to B, D, E + transmitPDU(sdu, 'A'); // send message to router A + } + else if (service.equals(SEND_UNICAST)) { // send unicast request? + MCAST.serviceMode = MCAST.UNICAST; // set unicast mode + serviceDestinations = "BDE"; // set service destinations + sdu = new MCASTSdu("B", message); // create SDU to B + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + sdu = new MCASTSdu("D", message); // create SDU to D + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + sdu = new MCASTSdu("E", message); // create SDU to E + serviceEvents.addElement( // add copy comment + MCAST.comment(this, "copy")); + transmitPDU(sdu, 'A'); // send message to router A + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + return(serviceEvents); // return service events + } + + /** + Send an SDU. + + @param sdu SDU + */ + public void transmitPDU(PDU sdu) { + String destinations = // get destinations + ((MCASTSdu) sdu).getDestinations(); + ProtocolEntity entityDestination = // get protocol entity dest. + MCAST.getEntity(destinations.charAt(0)); + transmitPDU(sdu, entityDestination); // transmit PDU + } + + /** + Send an SDU to the given destination letter. + + @param sdu SDU + @param destination destination letter + */ + public void transmitPDU(PDU sdu, char destination) { + ProtocolEntity entityDestination = // get protocol entity dest. + MCAST.getEntity(destination); + transmitPDU(sdu, entityDestination); // transmit PDU + } + + /** + Send an SDU to the given destination entity. + + @param sdu SDU + @param destination destination entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + serviceEvents.addElement( // append receive event + receiveEvents.get(i)); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUX.java b/lab8_protocols/jasper/source/protocol/MUX.java new file mode 100755 index 0000000..f676b61 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUX.java @@ -0,0 +1,116 @@ +// MUX.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of (de)multiplexing. It handles both + synchronous and asynchronous modes. + + @author Kenneth J. Turner + @version 1.0 (23rd July 2010, KJT): initial version +*/ + +public class MUX extends Protocol { + + /** Default service count */ + private final static int COUNT = 2; + + /** Maximum service count */ + private final static int MAX = 10; + + /** Multiplexer */ + private MUXProtocol protocolA; + + /** Demultiplexer */ + private MUXProtocol protocolB; + + /** Sources */ + private MUXService userA; + + /** Sinks */ + private MUXService userB; + + /** + Constructor for a MUX object. + */ + public MUX() { + medium = new MUXMedium(); // create channel + userA = new MUXService("Sources"); // create sources + userB = new MUXService("Sinks"); // create sinks + protocolA = // create multiplexer + new MUXProtocol(medium, "Multiplexer"); + protocolB = // create demultiplexer + new MUXProtocol(medium, "Demultiplexer"); + userA.setProvider(protocolA); // set multiplexer for sources + userB.setProvider(protocolB); // set demultiplexer for sinks + protocolA.setUser(userA); // set sources for multiplexer + protocolA.setPeer(protocolB); // set (de)multiplexer pair + protocolB.setUser(userB); // set sinks for demultiplexer + protocolB.setPeer(protocolA); // set (de)multiplexer pair + entities = new Vector(); // create entity list + entities.addElement(userA); // add sources + entities.addElement(protocolA); // add multiplexer + entities.addElement(medium); // add channel + entities.addElement(protocolB); // add demultiplexer + entities.addElement(userB); // add sinks + userA.setMode(true); // initialise sources + userB.setMode(false); // initialise sinks + protocolA.setMode(false); // initialise asynchronous mux + protocolB.setMode(false); // initialise asynchronous demux + userA.setCount(COUNT); // initialise source count + userB.setCount(COUNT); // initialise sink count + } + + /** + Set a parameter of the MUX simulation. + + @param parameter parameter name (source/sinkCount, [a]synchronousMode) + @param value parameter value + */ + public void setParameter(String parameter, String value) { + try { + if (parameter.equals("serviceCount")) { // service count? + int count = Integer.parseInt(value); // get value as int (if any) + if (0 <= count && count <= MAX) { // service count within range? + userA.setCount(count); // set service count + userB.setCount(count); // set service count + protocolA.initialise(); // re-initialise multiplexer + protocolB.initialise(); // re-initialise demultiplexer + } + else // service count out of range + System.err.println( // report error + "CSMA.setParameter: service count '" + value + "' out of range"); + } + else if (parameter.equals("serviceMode")) { // service mode? + boolean asynchronous = false; // initialise async setting + if (value.equalsIgnoreCase("true")) // true? + asynchronous = true; // note as synchronous + else if (!value.equalsIgnoreCase("false")) // not false + System.err.println( // report error + "MUX.setParameter: invalid asynchronous setting '" + value + "'"); + protocolA.setMode(asynchronous); // set multiplexer mode + protocolB.setMode(asynchronous); // set demultiplexer mode + } + else if (parameter.equals("serviceOverwrite")) { // service overwrite? + boolean overwrite = false; // initialise overwrite setting + if (value.equalsIgnoreCase("true")) // true? + overwrite = true; // note as overwriting + else if (!value.equalsIgnoreCase("false")) // not false + System.err.println( // report error + "MUX.setParameter: invalid overwrite setting '" + value + "'"); + userA.setOverwrite(overwrite); // set overwriting setting + userB.setOverwrite(overwrite); // set overwriting setting + } + } + catch (NumberFormatException e) { // count format exception? + System.err.println( // report error + "MUX.setParameter: service count '" + value + "' not an integer"); + } + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUXMedium.java b/lab8_protocols/jasper/source/protocol/MUXMedium.java new file mode 100755 index 0000000..079bc68 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUXMedium.java @@ -0,0 +1,29 @@ +// MUXMedium.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines the channel between (de)multiplexers. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010): initial version +*/ + +public class MUXMedium extends Medium { + + /** Medium name */ + private final static String NAME = "Channel"; + + /** + Gets the medium name. + + @return medium name + */ + public String getName() { + return(NAME); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUXPdu.java b/lab8_protocols/jasper/source/protocol/MUXPdu.java new file mode 100755 index 0000000..049af6e --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUXPdu.java @@ -0,0 +1,126 @@ +// MUXPdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines (de)multiplexer protocol messages. + + @author Kenneth J. Turner + @version 1.0 (23rd July 2010, KJT): initial version +*/ +public class MUXPdu extends PDU { + + /** Protocol PDU type */ + public final static String BLANK = "-"; + + /** Protocol PDU type */ + public final static String TYPE = "DT"; + + /** Protocol SDU list */ + private String sduList[]; + + /** + Constructor for a (de)multiplexer PDU for the required number of SDUs. + */ + public MUXPdu() { + super(TYPE); // construct with type + sduList = new String[MUXService.serviceCount]; // initialise SDU list + } + + /** + Get the PDU list of SDUs as a comma-separated string. + + @return comma-separated list of SDUs + */ + public String getData() { + String dataList = ""; // initialise SDU data list + for (int i = 0; i < sduList.length; i++) { // go through SDU data + if (i > 0) // not first SDU? + dataList += ","; // append a comma + String data = sduList[i]; // get SDU data + if (data == null) // SDU data missing? + data = BLANK; // set data blank + dataList += data; // append SDU data + } + return(dataList); // return SDU data list + } + + /** + Return the label for an arrow representing a (de)multiplexer PDU in a time + sequence diagram. + + @return PDU label + */ + public String getLabel() { + String label; // declare protocol label + if (MUXProtocol.protocolMode) { // asynchronous? + int first = firstSDU(); // get first SDU label + if (first >= 0) // first SDU known? + label = first + "," + sduList[first]; // set first index and data + else // first SDU unknown + label = "?,?"; // set unknown index and data + } + else // synchronous + label = getData(); // set type and SDU data list + label = type + "(" + label + ")"; // set protocol label + return(label); // return protocol label + } + + /** + Return SDU for given entity. + + @param entity entity index + @return SDU (or null if no such entity) + */ + public String getSDU(int entity) { + return(0 <= entity && entity < sduList.length ? sduList[entity] : null); + } + + /** + Return SDU list. + + @return SDU list + */ + public String[] getSDUs() { + return(sduList); + } + + /** + Return index of first available SDU. + + @return index of first available SDU (-1 means none) + */ + public int firstSDU() { + int index = -1; // initialise index + for (int i = 0; i < sduList.length; i++) { // go through SDU data + if (sduList[i] != null) { // data present? + index = i; // note data available + break; // leave loop + } + } + return(index); // return index + } + + /** + Convert PDU to string. + + @return PDU as string + */ + public String toString() { + return("PDU >"); + } + + /** + Set source SDU data. + + @param source source entity index + @param sdu source data + */ + public void setSDU(int source, String sdu) { + sduList[source] = sdu; // set SDU data for source + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUXProtocol.java b/lab8_protocols/jasper/source/protocol/MUXProtocol.java new file mode 100755 index 0000000..96ea7fe --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUXProtocol.java @@ -0,0 +1,280 @@ +// MUXProtocol.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the (de)multiplexing protocol. It handles + both synchronous and asynchronous modes. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ + +public class MUXProtocol implements ProtocolEntity { + + /** Service send offer */ + private final static String RECEIVE = "Send data to "; + + /** Protocol send offer */ + private final static String SEND = + "Multiplex and send "; + + /** Protocol send all available offer */ + private final static String SEND_ALL = SEND + "all available data"; + + /** Protocol send one offer */ + private final static String SEND_ONE = SEND + "the individual data"; + + /** Service source offer */ + private final static String SINK = "Sink "; + + /** Protocol events */ + private Vector protocolEvents; + + /** Protocol PDU received */ + private MUXPdu protocolIn; + + /** Protocol channel */ + private Medium protocolMedium; + + /** Protocol protocol mode (true = asynchronous, false = synchronous) */ + public static boolean protocolMode; + + /** Protocol name */ + private String protocolName; + + /** Protocol PDU sent */ + private MUXPdu protocolOut; + + /** Protocol peer */ + private MUXProtocol protocolPeer; + + /** Protocol user */ + private MUXService protocolUser; + + /** + Constructor for a (de)multiplexer. + + @param protocolMedium channel + @param protocolName protocol name + */ + public MUXProtocol(Medium protocolMedium, String protocolName) { + this.protocolName = protocolName; // set protocol name + this.protocolMedium = protocolMedium; // set protocol medium + initialise(); // initialise protocol + } + + /** + Return a protocol event with the given comment. + + @param comment protocol comment + @return protocol event + */ + private ProtocolEvent comment(String comment) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, comment)); + } + + /** + Get the protocol mode. + + @return protocol mode (true = asynchronous, false = synchronous) + */ + public boolean getMode() { + return(protocolMode); // return protocol mode + } + + /** + Get the protocol name. + + @return protocol name + */ + public String getName() { + return(protocolName); // return protocol name + } + + /** + Get the protocol peer. + + @return protocol peer + */ + public MUXProtocol getPeer() { + return(protocolPeer); // return protocol peer + } + + /** + Get protocol input. + + @return protocol input + */ + public MUXPdu getProtocolIn() { + return(protocolIn); // return protocol input + } + + /** + Get protocol output. + + @return protocol output + */ + public MUXPdu getProtocolOut() { + return(protocolOut); // return protocol output + } + + /** + Return services currently offered. + + @return list of protocol services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise protocol services + int firstSDU = protocolOut.firstSDU(); // get first SDU index + if (firstSDU >= 0) { // SDU available? + if (protocolMode) // asynchronous? + list.add(SEND_ONE); // append send one offer + else // synchronous + list.add(SEND_ALL); // append send all offer + } + String[] sduList = // get SDU list from PDU + ((MUXPdu) protocolIn).getSDUs(); + for (int i = 0; i < sduList.length; i++) { // go through SDUs + String data = sduList[i]; // get SDU data + if (data != null) // SDU available? + list.add(RECEIVE + SINK + i); // append protocol receive offer + } + return (list); // return protocol service list + } + + /** + Initialise the protocol entity. + */ + public void initialise() { + protocolEvents = new Vector();// initialise protocol events + protocolIn = new MUXPdu(); // initialise protocol input + protocolOut = new MUXPdu(); // initialise protocol output + } + + /** + Perform service. + + @param service service request + @return resulting protocol events + */ + public Vector performService(String service) { + protocolEvents = new Vector(); // initialise protocol events + if (service.startsWith(SEND)) { // send all request? + protocolEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.TRANSMIT, protocolOut)); + protocolEvents.addElement( // add receive event + new ProtocolEvent(ProtocolEvent.RECEIVE, protocolOut)); + transmitPDU(protocolOut, protocolPeer); // send protocol PDU + protocolOut = new MUXPdu(); // re-initialise sent PDU + } + else if (service.startsWith(RECEIVE)) { // receive request? + int sinkStart = // get start of sink index + service.indexOf(SINK) + SINK.length(); + int sink = // get source index + Integer.parseInt(service.substring(sinkStart)); + String[] sduList = protocolIn.getSDUs(); // get SDU list from PDU + String data = sduList[sink]; // get SDU data + MUXSdu sdu = new MUXSdu(sink, data); // create SDU + transmitPDU(sdu, protocolUser); // send to service user + protocolIn.setSDU(sink, null); // remove sink SDU data + } + return(protocolEvents); // return protocol events + } + + /** + Receive PDU. + + @param pdu PDU + @return resulting protocol events + */ + public Vector receivePDU(PDU pdu) { + protocolEvents = new Vector(); // initialise event list + if (pdu != null) { // non-empty PDU? + String pduType = pdu.type; // get PDU type + if (pduType.equals(MUXSdu.TYPE)) { // service user message? + int source = ((MUXSdu) pdu).entity; // get source index + String data = ((MUXSdu) pdu).sdu; // get service user data + String comment; // declare protocol comment + if (protocolMode) { // asynchronous? + comment = // set comment from presence + protocolOut.firstSDU() >= 0 ? "Overwritten" : "Stored"; + protocolOut = new MUXPdu(); // initialise protocol output + } + else // synchronous + comment = // set comment from presence + protocolOut.getSDU(source) != null ? "Overwritten" : "Stored"; + protocolOut.setSDU(source, data); // store source SDU data + protocolEvents.addElement( // add receive comment event + comment(comment)); + } + else if (pduType.equals(MUXPdu.TYPE)) { // protocol peer message? + String comment = // set comment from presence + protocolIn.firstSDU() >= 0 ? "Overwritten" : "Stored"; + protocolIn = (MUXPdu) pdu; // store received PDU + protocolEvents.addElement( // add receive comment event + comment(comment)); + } + } + return(protocolEvents); // return protocol events + } + + /** + Set the protocol mode. + + @param protocolMode protocol mode (true = asynchronous, + false = synchronous) + */ + public void setMode(boolean protocolMode) { + this.protocolMode = protocolMode; // set protocol mode + + } + + /** + Set the protocol peer. + + @param protocolPeer protocol peer + */ + public void setPeer(ProtocolEntity protocolPeer) { + this.protocolPeer = // set protocol peer + (MUXProtocol) protocolPeer; + } + + /** + Set the protocol service. + + @param protocolUser protocol service user + */ + public void setUser(ProtocolEntity protocolUser) { + this.protocolUser = // set service user + (MUXService) protocolUser; + } + + /** + Send PDU. + + @param pdu PDU + @param destination protocol destination + */ + public void transmitPDU(PDU pdu, ProtocolEntity destination) { + pdu.setSource(this); // set protocol entity as source + pdu.setDestination(destination); // set message destination + Vector receiveEvents; // declare receive events + if (destination == protocolPeer) { // for protocol peer? + receiveEvents = // receive PDU at medium + protocolPeer.receivePDU(pdu); + } + else // for service user? + receiveEvents = // receive PDU at user + protocolUser.receivePDU(pdu); + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + protocolEvents.addElement( // append receive event + receiveEvents.get(i)); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUXSdu.java b/lab8_protocols/jasper/source/protocol/MUXSdu.java new file mode 100755 index 0000000..4dda674 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUXSdu.java @@ -0,0 +1,55 @@ +// MUXSdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines (de)multiplexer service messages. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ + +public class MUXSdu extends PDU { + + /** Service SDU type */ + public final static String TYPE = "DATA"; + + /** Service entity */ + public int entity; + + /** Service message */ + public String sdu; + + /** + Constructor for a source/sink service message. + + @param entity service entity index + @param sdu SDU data + */ + public MUXSdu(int entity, String sdu) { + super(TYPE); // construct with type + this.entity = entity; // set entity index + this.sdu = sdu; // set SDU data + } + + /** + Return the label for an arrow representing a (de)multiplexer SDU in a time + sequence diagram. + + @return the label name + */ + public String getLabel() { + return(type + "(" + entity + "," + sdu + ")"); + } + + /** + Convert SDU to string. + */ + public String toString() { + return("SDU "); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/MUXService.java b/lab8_protocols/jasper/source/protocol/MUXService.java new file mode 100755 index 0000000..a70d84d --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/MUXService.java @@ -0,0 +1,198 @@ +// MUXService.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports sources/sinks for (de)multiplexing. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ + +public class MUXService implements ProtocolEntity { + + /** Service send offer */ + private final static String SEND = "Send data from "; + + /** Service source offer */ + private final static String SOURCE = "Source "; + + /** Service message count */ + private int messageCount; + + /** PDU sent by service */ + private PDU sduSent; + + /** Service entity count */ + public static int serviceCount = 2; + + /** Service mode (true = source, false = sink) */ + public boolean serviceMode; + + /** Service entity name */ + private String serviceName; + + /** Service overwriting (true = overwrite, false = no overwrite) */ + public static boolean serviceOverwrite; + + /** Service provider */ + private MUXProtocol serviceProvider; + + /** Service provider events */ + private Vector serviceEvents; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public MUXService(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceMode) { // source mode? + int first = // get protocol peer's first SDU + serviceProvider.getPeer().getProtocolIn().firstSDU(); + if (first == -1 || // protocol peer has no data or + serviceOverwrite) { // overwrite? + for (int i = 0; i < serviceCount; i++) {// go through sources + String data = // get SDU data for source + serviceProvider.getProtocolOut().getSDU(i); + first = // get provider's first SDU + ((MUXProtocol) serviceProvider).getProtocolOut().firstSDU(); + boolean mode = // get provider mode + serviceProvider.getMode(); + if ((!mode && data == null) || // sync and no source data or + (mode && first == -1) || // async and no provider data or + serviceOverwrite) { // overwrite? + String service = SEND + SOURCE + i; // send from source service + list.addElement(service); // add service to list + } + } + } + } + return(list); // return service list + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.startsWith(SEND)) { // send request? + String message = "D" + messageCount; // create data message + int sourceStart = // get start of source index + service.indexOf(SOURCE) + SOURCE.length(); + int source = // get source index + Integer.parseInt(service.substring(sourceStart)); + PDU sdu = new MUXSdu(source, message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + serviceEvents.addElement( // deliver SDU + new ProtocolEvent(ProtocolEvent.DELIVER, sdu)); + return(serviceEvents); // return service events + } + + /** + Set the service count. + + @param serviceCount service count + */ + public void setCount(int serviceCount) { + this.serviceCount = serviceCount; // set service count + } + + /** + Set the service mode. + + @param serviceMode service mode (true = source, false = sink) + */ + public void setMode(boolean serviceMode) { + this.serviceMode = serviceMode; // set service mode + } + + /** + Set service overwriting. + + @param serviceOverwrite service overwriting (true = overwrite, + false = no overwrite) + */ + public void setOverwrite(boolean serviceOverwrite) { + this.serviceOverwrite = serviceOverwrite; // set overwriting setting + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = // set service provider + (MUXProtocol) serviceProvider; + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + for (int i = 0; i < receiveEvents.size(); i++) // go through receive events + serviceEvents.addElement( // append receive event + receiveEvents.get(i)); + sduSent = sdu; // stored the sent SDU + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/SMTP.java b/lab8_protocols/jasper/source/protocol/SMTP.java new file mode 100755 index 0000000..e53633c --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SMTP.java @@ -0,0 +1,27 @@ +// SMTP.java (C) K. J. Turner, P. K. Johnson 04/03/06 + +// Simple Mail Transfer Protocol + +package protocol; + +import java.util.*; +import support.*; + +public class SMTP extends Protocol { + + private SMTPSender client; + private SMTPReceiver server; + + public SMTP() { + medium = new Medium(); + client = new SMTPSender(medium, "Client"); + server = new SMTPReceiver(medium, "Server"); + client.setPeer(server); + server.setPeer(client); + entities = new Vector(); + entities.addElement(client); + entities.addElement(medium); + entities.addElement(server); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SMTPReceiver.java b/lab8_protocols/jasper/source/protocol/SMTPReceiver.java new file mode 100755 index 0000000..e3fc72d --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SMTPReceiver.java @@ -0,0 +1,122 @@ +// SMTPReceiver.java (C) K. J. Turner, P. K. Johnson 04/03/06 + +// Simple Mail Transfer Protocol receiver (server) + +package protocol; + +import java.util.Vector; +import support.*; + +public class SMTPReceiver implements ProtocolEntity { + private PDU pduReceived; + private ProtocolEntity peer; + private Medium medium; + private String name; + private PDU pduSent; + + final static String clientData = "DATA"; + final static String clientFrom = "MAIL FROM: sender"; + final static String clientHello = "HELO client"; + final static String clientMail = "Mail message"; + final static String clientQuit = "QUIT"; + final static String clientRecipient = "RCPT TO: recipient"; + final static String invalidRecipient = "550 Recipient invalid"; + final static String invalidSender = "550 Sender invalid"; + final static String okRecipient = "250 Recipient OK"; + final static String okSender = "250 Sender OK"; + final static String serverAccept = "250 Message accepted"; + final static String serverClosing = "221 Server Closing"; + final static String serverHello = "250 Server hello to client"; + final static String serverReady = "220 Server ready"; + final static String serverSend = "354 Send mail"; + final static String tcpConnect = "TCP connect"; + final static String tcpDisconnect = "TCP disconnect"; + + public SMTPReceiver(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + String pduType; + if (pduReceived != null) { // non-null PDU received? + pduType = pduReceived.type; // get PDU type + if (pduType.equals(clientData)) // got client data? + list.addElement(serverSend); // reply server send + else if (pduType.startsWith(clientFrom)) { // got sender? + list.addElement(okSender); // reply sender OK + list.addElement(invalidSender); // reply sender invalid + } + else if (pduType.equals(clientHello)) // got client hello? + list.addElement(serverHello); // reply server hello + else if (pduType.equals(clientMail)) // got client mail message? + list.addElement(serverAccept); // reply server accept + else if (pduType.equals(clientQuit)) // got client quit? + list.addElement(serverClosing); // reply server closing + else if (pduType.startsWith(clientRecipient)) { // got recipient? + list.addElement(okRecipient); // reply recipient OK + list.addElement(invalidRecipient); // reply recipient invalid + } + else if (pduType.equals(tcpConnect)) // got TCP disconnect? + list.addElement(serverReady); // reply server ready? + // no action on TCP disconnect + } + return(list); + } + + public void initialise() { + pduReceived = null; + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.equals(invalidRecipient)) // send recipient invalid? + transmitPDU(new PDU(invalidRecipient), peer); + else if (s.equals(invalidSender)) // send sender invalid? + transmitPDU(new PDU(invalidSender), peer); + else if (s.equals(okRecipient)) // send recipient OK? + transmitPDU(new PDU(okRecipient), peer); + else if (s.equals(okSender)) // send sender OK? + transmitPDU(new PDU(okSender), peer); + else if (s.equals(serverAccept)) // send message accepted? + transmitPDU(new PDU(serverAccept), peer); + else if (s.equals(serverClosing)) // send server closing? + transmitPDU(new PDU(serverClosing), peer); + else if (s.equals(serverHello)) // send server hello? + transmitPDU(new PDU(serverHello), peer); + else if (s.equals(serverReady)) // send server ready? + transmitPDU(new PDU(serverReady), peer); + else if (s.equals(serverSend)) // send server send? + transmitPDU(new PDU(serverSend), peer); + if (pduSent != null) { // PDU to send? + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + events.addElement(new ProtocolEvent(ProtocolEvent.RECEIVE, pduSent)); + } + pduSent = null; // nullify just in case + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + pduSent = pdu; + this.peer.receivePDU(pdu); + pduReceived = null; + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SMTPSender.java b/lab8_protocols/jasper/source/protocol/SMTPSender.java new file mode 100755 index 0000000..4dbff25 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SMTPSender.java @@ -0,0 +1,177 @@ +// SMTPSender.java (C) K. J. Turner, P. K. Johnson 04/03/06 + +// Simple Mail Transfer Protocol sender (client) + +package protocol; + +import java.util.Vector; +import support.*; + +public class SMTPSender implements ProtocolEntity { + + private PDU pduReceived; + private PDU pduSent; + private ProtocolEntity peer; + private Medium medium; + private String name; + + final static String clientData = "DATA"; + final static String clientFrom = "MAIL FROM: sender"; + final static String clientHello = "HELO client"; + final static String clientMail = "Mail message"; + final static String clientQuit = "QUIT"; + final static String clientRecipient = "RCPT TO: recipient"; + final static String invalidRecipient = "550 Recipient invalid"; + final static String invalidSender = "550 Sender invalid"; + final static String okRecipient = "250 Recipient OK"; + final static String okSender = "250 Sender OK"; + final static String serverAccept = "250 Message accepted"; + final static String serverClosing = "221 Server Closing"; + final static String serverHello = "250 Server hello to client"; + final static String serverReady = "220 Server ready"; + final static String serverSend = "354 Send mail"; + final static String tcpConnect = "TCP connect"; + final static String tcpDisconnect = "TCP disconnect"; + + int state; // protocol state + + final static int idle = 0; // no connection + final static int waitReady = 1; // await server ready + final static int waitHello = 2; // await server hello + final static int ready = 3; // ready for mail + final static int waitSender = 4; // await sender answer + final static int waitRecipient = 5; // await recip answer + final static int waitSend = 6; // await server send + final static int waitAccept = 7; // await server accept + final static int waitClose = 8; // await server closing + + int person = 1; // sender/recipient + int recipients; // number of recipients + + public SMTPSender(Medium m, String name) { + this.name = name; + medium = m; + } + + public String getName() { + return(name); + } + + public Vector getServices() { + Vector list = new Vector(); + String pduType; + if (state == idle) // waiting to connect? + list.addElement(tcpConnect); // connect + else if (pduReceived != null) { // non-empty reply? + pduType = pduReceived.type; // get PDU type + if (state == waitReady && // awaiting server ready? + pduType.equals(serverReady)) // reply server ready? + list.addElement(clientHello); // send hello from client + else if (state == waitHello && // awaiting server hello? + pduType.equals(serverHello)) // reply server hello? + state = ready; // now ready + else if (state == waitSender) { // awaiting sender response? + if (pduType.equals(okSender)) { // sender is OK? + recipients = 0; // no recipients yet + state = waitSend; // now ready to send + } + else if (pduType.equals(invalidSender)) // sender is invalid? + state = ready; // back to ready + } + else if (state == waitRecipient && // awaiting recipient response? + (pduType.equals(okRecipient) || // recipient is OK or ... + pduType.equals(invalidRecipient))) { // not? + if (pduType.equals(okRecipient)) // recipient is OK? + recipients++; // add to recipients + state = waitSend; // still ready to send + } + else if (state == waitSend && // awaiting server send? + pduType.equals(serverSend)) { // response is server send? + list.addElement(clientMail); // send client mail message + state = waitAccept; // wait for server accept + } + else if (state == waitAccept && // awaiting server accept? + pduType.equals(serverAccept)) { // reply is server accept? + state = ready; // back to ready + } + else if (state == waitClose && // awaiting server closing? + pduType.equals(serverClosing)) { // reply is server closing? + list.addElement(tcpDisconnect); // disconnect + } + if (state == ready) { // ready? + list.addElement(clientFrom + person++); // propose sender + list.addElement(clientQuit); // propose quit + } + else if (state == waitSend) { // waiting to send? + list.addElement(clientRecipient + person++); // propose recipient + if (recipients > 0) // recipients? + list.addElement(clientData); // propose to send data + } + } + return(list); + } + + public void initialise() { + state = idle; // initialise state + pduReceived = null; // note no PDU received + person = 1; // initialise sender/recipient + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.equals(clientData)) { // send client data? + transmitPDU(new PDU(clientData), peer); // send client data + state = waitSend; // await server send + } + else if (s.startsWith(clientFrom)) { // send sender? + transmitPDU(new PDU(s), peer); // send sender + state = waitSender; // await sender response + } + else if (s.equals(clientHello)) { // send client hello? + transmitPDU(new PDU(s), peer); // send client hello + state = waitHello; // await server hello + } + else if (s.equals(clientMail)) // send client mail message? + transmitPDU(new PDU(clientMail), peer); // send client mail message + else if (s.equals(clientQuit)) { // send client quit? + transmitPDU(new PDU(clientQuit), peer); // send client quit + state = waitClose; // await server closing + } + else if (s.startsWith(clientRecipient)) { // send recipient? + transmitPDU(new PDU(s), peer); // send recipient + state = waitRecipient; // await recipient response + } + if (s.equals(tcpConnect)) { // send TCP connect? + transmitPDU(new PDU(tcpConnect), peer); // send TCP connect + state = waitReady; // await server ready + } + else if (s.equals(tcpDisconnect)) { // send TCP disconnect? + transmitPDU(new PDU(tcpDisconnect), peer); // send TCP disconnect + state = idle; // back to start + } + if (pduSent != null) { // PDU to send? + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + events.addElement(new ProtocolEvent(ProtocolEvent.RECEIVE, pduSent)); + } + pduSent = null; // nullify just in case + return(events); + } + + public Vector receivePDU(PDU pdu) { + pduReceived = pdu; + return(new Vector()); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + pduSent = pdu; + this.peer.receivePDU(pdu); + pduReceived = null; + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/STACK.java b/lab8_protocols/jasper/source/protocol/STACK.java new file mode 100755 index 0000000..101b350 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACK.java @@ -0,0 +1,68 @@ +// STACK.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for simulation of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ +public class STACK extends Protocol { + + /** Application layer */ + private STACKApplication userA; + + /** Transport layer */ + private STACKTransport userB; + + /** Network layer */ + private STACKNetwork userC; + + /** Data link layer */ + private STACKDataLink userD; + + /** Physical layer */ + private STACKPhysical userE; + + /** Medium */ + private STACKMedium medium; + + /** + Constructor for a STACK object. + */ + public STACK() { + userA = new STACKApplication("Application");// create application layer + userB = new STACKTransport("Transport"); // create transport layer + userC = new STACKNetwork("Network"); // create network layer + userD = new STACKDataLink("Data Link"); // create data link layer + userE = new STACKPhysical("Physical"); // create physical layer + medium = new STACKMedium(); // create medium + + userA.setProvider(userB); // set application->transport + userB.setProvider(userC); // set transport->network + userC.setProvider(userD); // set network->data link + userD.setProvider(userE); // set data link->physical + userE.setProvider(medium); // set physical->medium + + userB.setUser(userA); // set transport->application + userC.setUser(userB); // set network->transport + userD.setUser(userC); // set data link->network + userE.setUser(userD); // set physical->data link + medium.setUser(userE); // set medium->physical + + entities = new Vector(); // create entity list + entities.addElement(userA); // add application layer + entities.addElement(userB); // add transport layer + entities.addElement(userC); // add network layer + entities.addElement(userD); // add data link layer + entities.addElement(userE); // add physical layer + entities.addElement(medium); // add medium + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKApplication.java b/lab8_protocols/jasper/source/protocol/STACKApplication.java new file mode 100755 index 0000000..00282e0 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKApplication.java @@ -0,0 +1,157 @@ +// STACKApplication.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the application layer of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ +public class STACKApplication implements ProtocolEntity { + + /** Service message prefix */ + private final static String PREFIX = "A"; + + /** Service send offer */ + private final static String SEND = + "Send application message to transport layer"; + + /** Service message count */ + private int messageCount; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private ProtocolEntity serviceProvider; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public STACKApplication(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceOut == null) // no SDU sent? + list.addElement(SEND); // add send SDU service to list + return(list); // return service list + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.equals(SEND)) { // send request? + String message = PREFIX + messageCount; // create service message + serviceOut = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, serviceOut)); + transmitPDU(serviceOut, serviceProvider); // send service message + messageCount++; // increment message count + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + ProtocolEntity sduSource = sdu.source; // get SDU source + String sduType = sdu.type; // get SDU type + if (sduSource.equals(serviceUser)) { // service user message? + serviceOut = sdu; // store SDU from service user + } + else if (sduSource.equals(serviceProvider)) // service provider message? + serviceOut = null; // re-initialise SDU out + } + return(serviceEvents); // return service events + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = serviceProvider; // set service provider + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKDataLink.java b/lab8_protocols/jasper/source/protocol/STACKDataLink.java new file mode 100755 index 0000000..e6f1356 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKDataLink.java @@ -0,0 +1,180 @@ +// STACKDataLink.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the transport layer of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ +public class STACKDataLink implements ProtocolEntity { + + /** Service message prefix (and suffix) */ + private final static String PREFIX = "L"; + + /** Service receive offer */ + private final static String RECEIVE = + "Send data link message to network layer"; + + /** Service send offer */ + private final static String SEND = + "Send data link message to physical layer"; + + /** Service message count */ + private int messageCount; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private ProtocolEntity serviceProvider; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public STACKDataLink(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceOut != null) // SDU from service user? + list.addElement(SEND); // add send SDU service to list + else if (serviceIn != null) // SDU from service provider? + list.addElement(RECEIVE); // add send SDU service to list + return(list); // return service list + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.equals(SEND)) { // send request? + String message = // create service message + PREFIX + messageCount + ":" + serviceOut.getSDU() + + ":" + PREFIX + messageCount; + PDU sdu = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + serviceOut = null; // re-initialise SDU out + } + else if (service.equals(RECEIVE)) { // receive request? + String message = serviceIn.getSDU(); // get SDU data + int colon = message.indexOf(':'); // get first colon location + if (colon >= 0) // colon present? + message = message.substring(colon + 1); // remove prefix + colon = message.lastIndexOf(':'); // get last colon location + if (colon >= 0) // colon present? + message = message.substring(0, colon); // remove prefix + serviceIn = new STACKSdu(message); // create new message + transmitPDU(serviceIn, serviceUser); // send service message + serviceEvents.addElement( // add receive event + new ProtocolEvent(ProtocolEvent.SEND, serviceIn)); + serviceIn = null; // re-initialise SDU in + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + ProtocolEntity sduSource = sdu.source; // get SDU source + String sduType = sdu.type; // get SDU type + if (sduSource.equals(serviceUser)) { // service user message? + serviceOut = sdu; // store SDU from service user + } + else if (sduSource.equals(serviceProvider)) // service provider message? + serviceIn = sdu; // store SDU from provider + } + return(serviceEvents); // return service events + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = serviceProvider; // set service provider + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKMedium.java b/lab8_protocols/jasper/source/protocol/STACKMedium.java new file mode 100755 index 0000000..56ec8e0 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKMedium.java @@ -0,0 +1,114 @@ +// STACKMedium.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that defines the medium for a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010): initial version +*/ +public class STACKMedium extends Medium { + + /** Service send offer */ + private final static String SEND = + "Send medium message to physical layer"; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for protocol stack medium. + */ + public STACKMedium() { + initialise(); // initialise service entity + } + + /** + Return services currently offered. + + @return list of services (empty) + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceIn != null) // SDU from medium? + list.addElement(SEND); // add send SDU service to list + return(list); // return service list + } + + /** + Initialise the service entity. + */ + public void initialise() { + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.startsWith(SEND)) { // send request? + String message = serviceIn.getSDU(); // create service message + PDU sdu = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceUser); // send service message + serviceIn = null; // re-initialise SDU in + } + return(serviceEvents); // return service events + } + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceIn = sdu; // store SDU from service + serviceEvents = new Vector();// initialise service events + return(serviceEvents); // return service events + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKNetwork.java b/lab8_protocols/jasper/source/protocol/STACKNetwork.java new file mode 100755 index 0000000..89c06a2 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKNetwork.java @@ -0,0 +1,176 @@ +// STACKNetwork.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the transport layer of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ +public class STACKNetwork implements ProtocolEntity { + + /** Service message prefix */ + private final static String PREFIX = "N"; + + /** Service receive offer */ + private final static String RECEIVE = + "Send network message to transport layer"; + + /** Service send offer */ + private final static String SEND = + "Send network message to data link layer"; + + /** Service message count */ + private int messageCount; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private ProtocolEntity serviceProvider; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public STACKNetwork(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceOut != null) // SDU from service user? + list.addElement(SEND); // add send SDU service to list + else if (serviceIn != null) // SDU from service provider? + list.addElement(RECEIVE); // add send SDU service to list + return(list); // return service list + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.equals(SEND)) { // send request? + String message = // create service message + PREFIX + messageCount + ":" + serviceOut.getSDU(); + PDU sdu = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + serviceOut = null; // re-initialise SDU out + } + else if (service.equals(RECEIVE)) { // receive request? + String message = serviceIn.getSDU(); // get SDU data + int colon = message.indexOf(':'); // get first colon location + if (colon >= 0) // colon present? + message = message.substring(colon + 1); // remove prefix + serviceIn = new STACKSdu(message); // create new message + transmitPDU(serviceIn, serviceUser); // send service message + serviceEvents.addElement( // add receive event + new ProtocolEvent(ProtocolEvent.SEND, serviceIn)); + serviceIn = null; // re-initialise SDU in + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + ProtocolEntity sduSource = sdu.source; // get SDU source + String sduType = sdu.type; // get SDU type + if (sduSource.equals(serviceUser)) { // service user message? + serviceOut = sdu; // store SDU from service user + } + else if (sduSource.equals(serviceProvider)) // service provider message? + serviceIn = sdu; // store SDU from provider + } + return(serviceEvents); // return service events + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = serviceProvider; // set service provider + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKPhysical.java b/lab8_protocols/jasper/source/protocol/STACKPhysical.java new file mode 100755 index 0000000..fcc7975 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKPhysical.java @@ -0,0 +1,177 @@ +// STACKPhysical.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the transport layer of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ +public class STACKPhysical implements ProtocolEntity { + + /** Service message prefix */ + private final static String PREFIX = "P"; + + /** Service receive offer */ + private final static String RECEIVE = + "Send physical message to data link layer"; + + /** Service send offer */ + private final static String SEND = + "Send physical message to medium"; + + /** Service message count */ + private int messageCount; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private ProtocolEntity serviceProvider; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public STACKPhysical(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceOut != null) // SDU from service user? + list.addElement(SEND); // add send SDU service to list + else if (serviceIn != null) // SDU from service provider? + list.addElement(RECEIVE); // add send SDU service to list + return(list); // return service list + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.equals(SEND)) { // send request? + String message = // create service message + PREFIX + messageCount + ":" + serviceOut.getSDU(); + PDU sdu = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + serviceOut = null; // re-initialise SDU out + } + else if (service.equals(RECEIVE)) { // receive request? + String message = serviceIn.getSDU(); // get SDU data + int colon = message.indexOf(':'); // get first colon location + if (colon >= 0) // colon present? + message = message.substring(colon + 1); // remove prefix + serviceIn = new STACKSdu(message); // create new message + transmitPDU(serviceIn, serviceUser); // send service message + serviceEvents.addElement( // add receive event + new ProtocolEvent(ProtocolEvent.SEND, serviceIn)); + serviceIn = null; // re-initialise SDU in + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + ProtocolEntity sduSource = sdu.source; // get SDU source + String sduType = sdu.type; // get SDU type + if (sduSource.equals(serviceUser)) { // service user message? + serviceOut = sdu; // store SDU from service user + } + else if (sduSource.equals(serviceProvider)) // service provider message? + serviceIn = sdu; // store SDU from provider + } + return(serviceEvents); // return service events + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = serviceProvider; // set service provider + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + + diff --git a/lab8_protocols/jasper/source/protocol/STACKSdu.java b/lab8_protocols/jasper/source/protocol/STACKSdu.java new file mode 100755 index 0000000..502d254 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKSdu.java @@ -0,0 +1,70 @@ +// STACKSdu.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class that defines SDUs for a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (23rd July 2010, KJT): initial version +*/ + +public class STACKSdu extends PDU { + + /** Service SDU type */ + public final static String TYPE = "DATA"; + + /** Service message */ + public String sdu; + + /** + Constructor for a source/sink service message. + + @param sdu SDU data + */ + public STACKSdu(String sdu) { + super(TYPE); // construct with type + this.sdu = sdu; // set SDU data + } + + /** + Return the label for an arrow representing a protocol stack SDU in a time + sequence diagram. + + @return the label name + */ + public String getLabel() { + return type + "(" + sdu + ")"; + } + + /** + Return the SDU. + + @return SDU + */ + public String getSDU() { + return(sdu); + } + + /** + Return the SDU. + + @param sdu SDU + */ + public void setSDU(String sdu) { + this.sdu = sdu; + } + + /** + Convert SDU to string. + + @return SDU as string + */ + public String toString() { + return("SDU "); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/STACKTransport.java b/lab8_protocols/jasper/source/protocol/STACKTransport.java new file mode 100755 index 0000000..d807e2e --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/STACKTransport.java @@ -0,0 +1,177 @@ +// STACKTransport.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class that supports the transport layer of a protocol stack. + + @author Kenneth J. Turner + @version 1.0 (20th July 2010, KJT): initial version +*/ + +public class STACKTransport implements ProtocolEntity { + + /** Service message prefix */ + private final static String PREFIX = "T"; + + /** Service receive offer */ + private final static String RECEIVE = + "Send transport message to application layer"; + + /** Service send offer */ + private final static String SEND = + "Send transport message to network layer"; + + /** Service message count */ + private int messageCount; + + /** Service provider events */ + private Vector serviceEvents; + + /** Service data unit input */ + private PDU serviceIn; + + /** Service data unit output */ + private PDU serviceOut; + + /** Service entity name */ + private String serviceName; + + /** Service provider */ + private ProtocolEntity serviceProvider; + + /** Service user */ + private ProtocolEntity serviceUser; + + /** + Constructor for sources/sinks. + + @param serviceName service name + */ + public STACKTransport(String serviceName) { + this.serviceName = serviceName; // set service name + initialise(); // initialise service entity + } + + /** + Return the service name. + + @return service name + */ + public String getName() { + return(serviceName); // return service name + } + + /** + Initialise the service entity. + */ + public void initialise() { + messageCount = 0; // initialise message count + serviceEvents = new Vector();// initialise service events + serviceIn = null; // initialise SDU input + serviceOut = null; // initialise SDU output + } + + /** + Return services currently offered. + + @return list of services + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + if (serviceOut != null) // SDU from service user? + list.addElement(SEND); // add send SDU service to list + else if (serviceIn != null) // SDU from service provider? + list.addElement(RECEIVE); // add send SDU service to list + return(list); // return service list + } + + /** + Perform service. + + @param service service request + @return resulting service events + */ + public Vector performService(String service) { + serviceEvents = new Vector();// initialise service events + if (service.equals(SEND)) { // send request? + String message = // create service message + PREFIX + messageCount + ":" + serviceOut.getSDU(); + PDU sdu = new STACKSdu(message); // create SDU + serviceEvents.addElement( // add send event + new ProtocolEvent(ProtocolEvent.SEND, sdu)); + transmitPDU(sdu, serviceProvider); // send service message + messageCount++; // increment message count + serviceOut = null; // re-initialise SDU out + } + else if (service.equals(RECEIVE)) { // receive request? + String message = serviceIn.getSDU(); // get SDU data + int colon = message.indexOf(':'); // get first colon location + if (colon >= 0) // colon present? + message = message.substring(colon + 1); // remove prefix + serviceIn = new STACKSdu(message); // create new message + transmitPDU(serviceIn, serviceUser); // send service message + serviceEvents.addElement( // add receive event + new ProtocolEvent(ProtocolEvent.SEND, serviceIn)); + serviceIn = null; // re-initialise SDU in + } + return(serviceEvents); // return service events + } + + /** + Receive an SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + serviceEvents = new Vector();// initialise service events + if (sdu != null) { // non-empty SDU? + ProtocolEntity sduSource = sdu.source; // get SDU source + String sduType = sdu.type; // get SDU type + if (sduSource.equals(serviceUser)) { // service user message? + serviceOut = sdu; // store SDU from service user + } + else if (sduSource.equals(serviceProvider)) // service provider message? + serviceIn = sdu; // store SDU from provider + } + return(serviceEvents); // return service events + } + + /** + Set the service provider. + + @param serviceProvider service provider + */ + public void setProvider(ProtocolEntity serviceProvider) { + this.serviceProvider = serviceProvider; // set service provider + } + + /** + Set the service user. + + @param serviceUser service user + */ + public void setUser(ProtocolEntity serviceUser) { + this.serviceUser = serviceUser; // set service user + } + + /** + Send an SDU. + + @param sdu SDU + @param dest destination protocol entity + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); // set source as this entity + sdu.setDestination(destination); // set destination + Vector receiveEvents = // receive SDU at destination + destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/SWP3.java b/lab8_protocols/jasper/source/protocol/SWP3.java new file mode 100755 index 0000000..1db960f --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP3.java @@ -0,0 +1,44 @@ +// SWP3.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class SWP3 extends Protocol { + + private int maxSeq = 7; + private int winSize = 3; + private SWP3Sender sender; + private SWP3Receiver receiver; + + public SWP3() { + medium = new Medium(); + sender = new SWP3Sender(maxSeq, winSize, medium); + receiver = new SWP3Receiver(maxSeq, winSize, medium); + sender.setPeer(receiver); + receiver.setPeer(sender); + entities = new Vector(); + entities.addElement(sender); + entities.addElement(medium); + entities.addElement(receiver); + } + + public void setParameter(String param, String value) { + super.setParameter(param, value); + try { + if (param.equals("maxSeq")) { + maxSeq = Integer.parseInt(value); + sender.setMaxSeq(maxSeq); + receiver.setMaxSeq(maxSeq); + } + else if (param.equals("winSize")) { + winSize = Integer.parseInt(value); + sender.setWindowSize(winSize); + receiver.setWindowSize(winSize); + } + } + catch (NumberFormatException e) {} + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP3Receiver.java b/lab8_protocols/jasper/source/protocol/SWP3Receiver.java new file mode 100755 index 0000000..e30c2d3 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP3Receiver.java @@ -0,0 +1,154 @@ +// SWP3Receiver.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package protocol; + +import java.util.Enumeration; +import java.util.Vector; +import support.*; + +public class SWP3Receiver implements ProtocolEntity { + + private int maxSeq; + private int winSize; + private int lowerWindowEdge; + private int upperWindowEdge; + private PDU pduReceived; + private boolean[] pduArrived; + private Medium medium; + private ProtocolEntity peer; + private Vector entityEvents; // events from entity + + public SWP3Receiver(int maxSeq, int winSize, Medium m) { + this.maxSeq = maxSeq; + this.winSize = winSize; + pduArrived = new boolean[maxSeq + 1]; + medium = m; + initialise(); + } + + private ProtocolEvent comment(String c) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, c)); + } + + public String getName() { + return("Receiver"); + } + + public Vector getServices() { + Vector list = new Vector(); + PDU pdu = pduReceived; + if (pdu != null && pdu.type.equals("DT")) { + int seq = pdu.seq; + if (isWithinWindow(seq)) { + int s = lowerWindowEdge; + for (int i = 0; i < winSize && pduArrived[s]; i++) { + s = inc(s); + } + list.addElement("Send AK(" + s + ")"); + } + else // duplicate seq. no. + list.addElement("Send AK(" + lowerWindowEdge +")"); + } + return(list); + } + + private int inc(int seq) { + return(mod(seq + 1)); + } + + public void initialise() { + for (int i = 0; i <= maxSeq; i++) pduArrived[i] = false; + lowerWindowEdge = 0; + upperWindowEdge = winSize; + pduReceived = null; + entityEvents = new Vector(); // empty entity events + } + + private boolean isWithinWindow(int seq) { + return + (lowerWindowEdge <= seq && seq < upperWindowEdge) || + (upperWindowEdge < lowerWindowEdge && + (seq >= lowerWindowEdge || seq < upperWindowEdge)); + } + + private int mod(int n) { + int base = maxSeq + 1; + n %= base; + return(n >= 0 ? n : n + base); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith("Send AK")) { + int startIndex = s.indexOf( '(' ) + 1; + int endIndex = s.indexOf( ')' ); + int seq = Integer.parseInt(s.substring(startIndex, endIndex)); + PDU pdu = new PDU("AK", seq); + transmitPDU(pdu, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + pduReceived = null; + } + for (Enumeration e = entityEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + if (pdu != null && pdu.type.equals("DT")) { + int seq = pdu.seq; + if (isWithinWindow(seq)) { + boolean alreadyBuffered = pduArrived[seq]; + boolean delivered = false; + pduArrived[seq] = true; + int s = lowerWindowEdge; + // deliver sequence of data messages to user + for (int i = 0; i < winSize && pduArrived[s]; i++) { + events.addElement(comment("DT(" + s + ") delivered")); + s = inc(s); + } + // clear pduArrived flags for messages passed to user + for (int i = lowerWindowEdge; i != s; i = inc(i)) { + pduArrived[i] = false; + if (i == seq) { + alreadyBuffered = true; + delivered = true; + } + } + if (alreadyBuffered && !delivered) + events.addElement(comment("Ignored")); + if (!alreadyBuffered) { + events.addElement(comment("Buffered")); + alreadyBuffered = true; + } + // update window edges + lowerWindowEdge = s; + upperWindowEdge = mod(lowerWindowEdge + winSize); + } + else { // duplicate sequence number received + events.addElement(comment("Ignored")); + } + } + pduReceived = pdu; + return(events); + } + + public void setMaxSeq(int maxSeq) { + this.maxSeq = maxSeq; + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void setWindowSize(int winSize) { + this.winSize = winSize; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + entityEvents = medium.receivePDU(pdu); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP3Sender.java b/lab8_protocols/jasper/source/protocol/SWP3Sender.java new file mode 100755 index 0000000..d167153 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP3Sender.java @@ -0,0 +1,220 @@ +// SWP3Sender.java + +package protocol; + +import java.util.Enumeration; // enumeration +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +/** + This is the class for a three-column Sliding Window Protocol sender. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +public class SWP3Sender implements ProtocolEntity, Timeouts { + + // SWP3 Sliding Window sender protocol entity + + private static final String SEND = "Send"; + private static final String RESEND = "Timeout - resend"; + private static final String BLOCKED = "Blocked"; + private static final String CLEAR = "Clear"; + + private int maxSeq; // maximum seq. no. + private int winSize; // transmit window + private int lowerWindowEdge; // ack seq. no. + private int upperWindowEdge; // next seq. no. + private boolean[] timerEnabled; + private Medium medium; + private ProtocolEntity peer; + private Vector services; + private boolean windowClosed; + private Vector entityEvents; // events from entity + + public SWP3Sender(int maxSeq, int winSize, Medium m) { + setMaxSeq(maxSeq); + setWindowSize(winSize); + medium = m; + initialise(); + } + + private void addService(String s) { + if (services.indexOf(s) < 0) + services.addElement(s); + } + + private ProtocolEvent comment(String c) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, c)); + } + + public String getName() { + return("Sender"); + } + + public Vector getServices() { + int seq; + int first = -1; + int last = -1; + // remove any previous resend actions + for (Enumeration e = services.elements(); e.hasMoreElements(); ) { + String s = (String)e.nextElement(); + if(s.startsWith(RESEND)) services.removeElement(s); + } + for (seq = lowerWindowEdge; isWithinWindow(seq); seq = inc(seq)) { + if (timerEnabled[seq]) { + if (first < 0) first = seq; + last = seq; + } + } + if (first >= 0) { // >=1 PDU may time out + // ensure there is no option to send new data until timeout dealt with + services.removeAllElements(); + if (last == first) // 1 PDU may be resent + addService(RESEND + " DT(" + first + ")"); + else // >1 PDU may be resent + addService(RESEND + " DT(" + first + ") - (" + last + ")"); + } + if (!windowClosed) { + addService(SEND + " DT(" + seq + ")"); + } + return(services); + } + + public boolean hasTimer(String type) { + return(true); + } + + private int inc(int seq) { + return(mod(seq + 1)); + } + + public void initialise() { + timerEnabled = new boolean[maxSeq + 1]; + for (int i = 0; i <= maxSeq; i++) timerEnabled[i] = false; + lowerWindowEdge = 0; + upperWindowEdge = 0; + services = new Vector(); + windowClosed = false; + entityEvents = new Vector(); // empty medium events + } + + private boolean isWithinWindow(int seq) { + return (lowerWindowEdge <= seq && seq < upperWindowEdge) || + (upperWindowEdge < lowerWindowEdge && + (seq >= lowerWindowEdge || seq < upperWindowEdge)); + } + + private int mod(int n) { + int base = maxSeq + 1; + n %= base; + return(n >= 0 ? n : n + base); + } + + public Vector performService(String s) { + int startIndex, endIndex; + String seqString; + Vector events = new Vector(); + if (s.startsWith(SEND)) { + PDU pdu = new PDU("DT", upperWindowEdge); + transmitPDU(pdu, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + removeService(s); + upperWindowEdge = inc(upperWindowEdge); + windowClosed = mod(upperWindowEdge - lowerWindowEdge) >= winSize; + if (windowClosed) + events.addElement(comment(BLOCKED)); + } + if (s.startsWith(RESEND)) { + // get sequence number of first data PDU to be resent + startIndex = s.indexOf( '(' ) + 1; + endIndex = s.indexOf( ')' ); + seqString = s.substring(startIndex, endIndex); + int seq1 = Integer.parseInt(seqString); + // get sequence number of last data PDU to be resent + startIndex = s.lastIndexOf( '(' ) + 1; + endIndex = s.lastIndexOf( ')' ); + seqString = s.substring(startIndex, endIndex); + int seq2 = Integer.parseInt(seqString); + releaseTimers(seq1, inc(seq2)); + // retransmit all unacknowledged data PDUs already sent and timed out + for (int p = seq1; p != inc(seq2); p = inc(p)) { + PDU pdu = new PDU("DT", p); + transmitPDU(pdu, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TIMEOUT, pdu)); + } + removeService(s); + } + for (Enumeration e = entityEvents.elements(); // get medium events + e.hasMoreElements(); ) + events.addElement( // add medium event + (ProtocolEvent) e.nextElement()); + return(events); + } + + private void releaseTimers(int lower, int upper) { + for (int seq = lower; seq != upper; seq = inc(seq)) { + timerEnabled[seq] = false; + } + } + + private void removeService(String s) { + if (services.isEmpty()) + return; + for (Enumeration e = services.elements(); e.hasMoreElements(); ) { + String s1 = (String)e.nextElement(); + if (s1.equals(s)) { + services.removeElement(s1); + break; + } + } + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + timerEnabled[pdu.seq] = enabled; + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + if (pdu != null && pdu.type.equals("AK")) { // respond to AK + int seq = pdu.seq; + if (isWithinWindow(mod(seq - 1))) { + releaseTimers(lowerWindowEdge, seq); + lowerWindowEdge = seq; // update sender window + if (windowClosed) { + // send window was full, indicate it is now clear: + events.addElement(comment(CLEAR)); + } + windowClosed = false; + } + } + return(events); + } + + public void setMaxSeq(int maxSeq) { + this.maxSeq = maxSeq; + } + + public void setWindowSize(int winSize) { + this.winSize = winSize; + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + entityEvents = medium.receivePDU(pdu); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP5.java b/lab8_protocols/jasper/source/protocol/SWP5.java new file mode 100755 index 0000000..7b5587b --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP5.java @@ -0,0 +1,53 @@ +// SWP5.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class SWP5 extends Protocol { + + private int maxSeq = 7; + private int winSize = 3; + private SWP5Service userA; + private SWP5Service userB; + private SWP5Sender sender; + private SWP5Receiver receiver; + + public SWP5() { + medium = new Medium(); + userA = new SWP5Service("User A", true); + userB = new SWP5Service("User B", false); + sender = new SWP5Sender(maxSeq, winSize, medium); + receiver = new SWP5Receiver(maxSeq, winSize, medium); + userA.setProvider(sender); + userB.setProvider(receiver); + sender.setUser(userA); + receiver.setUser(userB); + sender.setPeer(receiver); + receiver.setPeer(sender); + entities = new Vector(); + entities.addElement(userA); + entities.addElement(sender); + entities.addElement(medium); + entities.addElement(receiver); + entities.addElement(userB); + } + + public void setParameter(String param, String value) { + try { + if (param.equals("maxSeq")) { + maxSeq = Integer.parseInt(value); + sender.setMaxSeq(maxSeq); + receiver.setMaxSeq(maxSeq); + } + if (param.equals("winSize")) { + winSize = Integer.parseInt(value); + sender.setWindowSize(winSize); + receiver.setWindowSize(winSize); + } + } + catch (NumberFormatException e) {} + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP5Receiver.java b/lab8_protocols/jasper/source/protocol/SWP5Receiver.java new file mode 100755 index 0000000..5fb27dd --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP5Receiver.java @@ -0,0 +1,144 @@ +// SWP5Receiver.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class SWP5Receiver implements ProtocolEntity { + + private int maxSeq; + private int winSize; + private int lowerWindowEdge; + private int upperWindowEdge; + private boolean[] pduArrived; + private PDU[] recvBuffer; + private SWP5Service user; + private ProtocolEntity peer; + private Medium medium; + private Vector userEvents; + private int ackSeq; // next AK seq. no. + + public SWP5Receiver(int maxSeq, int winSize, Medium m) { + setMaxSeq(maxSeq); + setWindowSize(winSize); + medium = m; + initialise(); + } + + public void setMaxSeq(int maxSeq) { + this.maxSeq = maxSeq; + } + + public void setWindowSize(int winSize) { + this.winSize = winSize; + } + + public void initialise() { + recvBuffer = new PDU[maxSeq + 1]; + pduArrived = new boolean[maxSeq + 1]; + for (int i = 0; i <= maxSeq; i++) pduArrived[i] = false; + lowerWindowEdge = 0; + upperWindowEdge = winSize; + ackSeq = -1; + } + + private int inc(int seq) { + return(mod(seq + 1)); + } + + private int mod(int n) { + int base = maxSeq + 1; + n %= base; + return(n >= 0 ? n : n + base); + } + + private boolean isWithinWindow(int seq) { + return(lowerWindowEdge <= seq && seq < upperWindowEdge) || + (upperWindowEdge < lowerWindowEdge && + (seq >= lowerWindowEdge || seq < upperWindowEdge)); + } + + private ProtocolEvent comment(String c) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, c)); + } + + public String getName() { + return("Protocol B"); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void setUser(ProtocolEntity user) { + this.user = (SWP5Service)user; + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + if (pdu != null && pdu.type.equals("DT")) { + ackSeq = -1; + int seq = pdu.seq; + if (isWithinWindow(seq)) { + pduArrived[seq] = true; + recvBuffer[seq] = pdu; + seq = lowerWindowEdge; + // deliver sequence of data messages to user + for (int i = 0; i < winSize && pduArrived[seq]; i++) { + PDU pduSent = new PDU("DatInd", recvBuffer[seq].getSDU()); + transmitPDU(pduSent, user); + events.addElement( + new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); + seq = inc(seq); + } + ackSeq = seq; + // clear pduArrived flags for messages passed to user + for (int i = lowerWindowEdge; i != seq; i = inc(i)) + pduArrived[i] = false; + // update window edges + lowerWindowEdge = seq; + upperWindowEdge = mod(lowerWindowEdge + winSize); + } + else { // duplicate seq. no. + ackSeq = lowerWindowEdge; + events.addElement(comment("Ignored")); + } + } + return(events); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + if (dest == peer) + userEvents = medium.receivePDU(pdu); + else + userEvents = user.receivePDU(pdu); + } + + public Vector getServices() { + Vector services = new Vector(); + if (ackSeq >= 0) + services.addElement("Send AK(" + ackSeq + ")"); + return(services); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith("Send AK")) { + int startIndex = s.indexOf( '(' ) + 1; + int endIndex = s.indexOf( ')' ); + int seq = Integer.parseInt(s.substring(startIndex, endIndex)); + // send AK for next sequence number required + PDU ackPDU = new PDU("AK", seq); + transmitPDU(ackPDU, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, ackPDU)); + ackSeq = -1; + } + for (Enumeration e = userEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP5Sender.java b/lab8_protocols/jasper/source/protocol/SWP5Sender.java new file mode 100755 index 0000000..6ba9293 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP5Sender.java @@ -0,0 +1,200 @@ +// SWP5Sender.java + +package protocol; + +import java.util.*; +import support.*; + +/** + This is the class for a five-column Sliding Window Protocol sender. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +public class SWP5Sender implements ProtocolEntity, Timeouts { + + private static final String SEND = "Send"; + private static final String RESEND = "Timeout - resend"; + private static final String BLOCKED = "Blocked"; + private static final String CLEAR = "Clear"; + + private int maxSeq; // maximum seq. no. + private int winSize; // transmit window + private int lowerWindowEdge; // ack seq. no. + private int upperWindowEdge; // next seq. no. + private boolean[] timerEnabled; + private PDU[] recvBuffer; + private Medium medium; + private SWP5Service user; + private ProtocolEntity peer; + private Vector userEvents; + private boolean windowClosed; + + public SWP5Sender(int maxSeq, int winSize, Medium m) { + setMaxSeq(maxSeq); + setWindowSize(winSize); + medium = m; + initialise(); + } + + public void initialise() { + recvBuffer = new PDU[maxSeq + 1]; + timerEnabled = new boolean[maxSeq + 1]; + for (int i = 0; i <= maxSeq; i++) timerEnabled[i] = false; + lowerWindowEdge = 0; + upperWindowEdge = 0; + windowClosed = false; + } + + public void setMaxSeq(int maxSeq) { + this.maxSeq = maxSeq; + } + + public void setWindowSize(int winSize) { + this.winSize = winSize; + } + + private int inc(int seq) { + return(mod(seq + 1)); + } + + private int mod(int n) { + int base = maxSeq + 1; + n %= base; + return(n >= 0 ? n : n + base); + } + + private boolean isWithinWindow(int seq) { + return(lowerWindowEdge <= seq && seq < upperWindowEdge) || + (upperWindowEdge < lowerWindowEdge && + (seq >= lowerWindowEdge || seq < upperWindowEdge)); + } + + public String getName() { + return("Protocol A"); + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + timerEnabled[pdu.seq] = enabled; + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void setUser(ProtocolEntity user) { + this.user = (SWP5Service)user; + } + + private void releaseTimers(int lower, int upper) { + for (int seq = lower; seq != upper; seq = inc(seq)) { + timerEnabled[seq] = false; + } + } + + private ProtocolEvent comment(String c) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, c)); + } + + public boolean hasTimer(String type) { + return(true); + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + String pduType = ""; + if (pdu != null) pduType = pdu.type; + if (pduType.equals("DatReq")) { // DatReq from user + PDU pduSent = new PDU("DT", upperWindowEdge, pdu.getSDU()); + recvBuffer[upperWindowEdge] = pdu; + transmitPDU(pduSent, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + upperWindowEdge = inc(upperWindowEdge); + windowClosed = mod(upperWindowEdge - lowerWindowEdge) >= winSize; + if (windowClosed) { + events.addElement(comment(BLOCKED)); + } + } + if (pduType.equals("AK")) { // AK PDU from peer + int seq = pdu.seq; + if (isWithinWindow(mod(seq - 1))) { + releaseTimers(lowerWindowEdge, seq); + lowerWindowEdge = seq; // update sender window + if (windowClosed) { + // Send window was full, indicate it is now clear: + events.addElement(comment(CLEAR)); + } + windowClosed = false; // and set flag appropriately + } + } + user.setOKToSend(!windowClosed); + return(events); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + if (dest == peer) + userEvents = medium.receivePDU(pdu); + else + userEvents = user.receivePDU(pdu); + } + + public Vector getServices() { + Vector services = new Vector(); + int seq; + int first = -1; + int last = -1; + for (seq = lowerWindowEdge; isWithinWindow(seq); seq = inc(seq)) { + if (timerEnabled[seq]) { + if (first < 0) + first = seq; + last = seq; + } + } + if (first >= 0) { // >=1 PDU may time out + if (last == first) // only one PDU may be resent + services.addElement(RESEND + " DT(" + first + ")"); + else // more than one PDU may be resent + services.addElement(RESEND + " DT(" + first + ") - (" + last + ")"); + } + return(services); + } + + public Vector performService(String s) { + int startIndex, endIndex; + String seqString; + Vector events = new Vector(); + if (s.startsWith(RESEND)) { + // get sequence number of first data PDU to be resent + startIndex = s.indexOf( '(' ) + 1; + endIndex = s.indexOf( ')' ); + seqString = s.substring(startIndex, endIndex); + int seq1 = Integer.parseInt(seqString); + // get sequence number of last data PDU to be resent + startIndex = s.lastIndexOf( '(' ) + 1; + endIndex = s.lastIndexOf( ')' ); + seqString = s.substring(startIndex, endIndex); + int seq2 = Integer.parseInt(seqString); + releaseTimers(seq1, inc(seq2)); + // retransmit all unacknowledged data PDUs already sent and timed out + for (int p = seq1; p != inc(seq2); p = inc(p)) { + PDU pdu = new PDU("DT", p, recvBuffer[p].getSDU()); + transmitPDU(pdu, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TIMEOUT, pdu)); + } + } + for (Enumeration e = userEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/SWP5Service.java b/lab8_protocols/jasper/source/protocol/SWP5Service.java new file mode 100755 index 0000000..8b0c08f --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/SWP5Service.java @@ -0,0 +1,80 @@ +// SWP5Service.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class SWP5Service implements ProtocolEntity { + + public static final String DAT_REQ = "Data Request"; + + private ProtocolEntity provider; // protocol for service + private Vector providerEvents; + private PDU pduSent; + private String name; + private boolean sending; // sending/receiving + private int block; // data seq. no. + private boolean okToSend; // OK to send DatReq + + public SWP5Service(String name, boolean sending) { + this.name = name; + this.sending = sending; + initialise(); + } + + public void initialise() { + block = 0; + okToSend = true; + providerEvents = new Vector(); + } + + public String getName() { + return(name); + } + + public void setProvider(ProtocolEntity provider) { + this.provider = provider; + } + + // DatReq allowed only when okToSend is true + // (sender protocol window not closed) + + public void setOKToSend(boolean ok) { + okToSend = ok; + } + + // accept message but don't need to do anything with it + + public Vector receivePDU(PDU pdu) { + return(new Vector()); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + providerEvents = dest.receivePDU(pdu); + pduSent = pdu; + } + + public Vector getServices() { + Vector list = new Vector(); + if (sending && okToSend) + list.addElement("Send " + DAT_REQ + "(D" + block + ")"); + return(list); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (sending && s.startsWith("Send " + DAT_REQ)) { + String sdu = "D" + block; + transmitPDU(new PDU("DatReq", sdu), provider); + block++; + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + } + for (Enumeration e = providerEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/TCP.java b/lab8_protocols/jasper/source/protocol/TCP.java new file mode 100755 index 0000000..cb44a64 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TCP.java @@ -0,0 +1,200 @@ +// TCP.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the main class for the TCP simulation. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (26th July 2010, KJT): minor tidying; additions of constants + for initial values; addition of a loss rate; addition of + support for the slow-start TCP simulation +*/ +public class TCP extends Protocol { + + // role constants + + /** Peer-to-peer role */ + public final static int PEER = 0; + + /** Client-server role */ + public final static int CLIENT = 1; + + /** Server role */ + public final static int SERVER = 2; + + // protocol constants + + /** Default medium loss rate */ + public final static float LOSS_RATE = 0.2f; + + // protocol constants + + /** Default initial sequence number for protocol A */ + public final static int INITIAL_SEQUENCE_A = 0; + + /** Default initial sequence number for protocol A */ + public final static int INITIAL_SEQUENCE_B = 100; + + /** Default initial window size for protocol A */ + public final static int INITIAL_WINDOW_A = 500; + + /** Default initial window size for protocol B */ + public final static int INITIAL_WINDOW_B = 400; + + /** Default segment size for protocol */ + public final static int SEGMENT_SIZE = 200; + + // service constants + + /** Default message size for service A */ + public final static int MESSAGE_SIZE_A = 100; + + /** Default message size for service A */ + public final static int MESSAGE_SIZE_B = 300; + + // simulation variables + + /** Protocol type ("cs", "pp", "ss") */ + private static String protocolSubtype; + + /** Service entities A and B */ + private TCPService servA, servB; + + /** Protocol entities A and B */ + private TCPProtocol protA, protB; + + /** Role of A and B */ + private int roleA, roleB; + + /** Names of A and B */ + private String nameA, nameB; + + /** + Constructor for the TCP object + + @param type protocol type ("cs", "pp", "ss") + @exception unknown protocol type + */ + public TCP(String type) throws Exception { + protocolSubtype = // store protocol type in l. c. + type.toLowerCase(); + medium = new TCPMedium(); // construct medium + // medium.setMediumType(Medium.CONTROL_NOTHING); + TCPProtocol.setSegmentSize(SEGMENT_SIZE); // set protocol segment size + if (protocolSubtype.equals("cs")) { // client-server subtype? + roleA = CLIENT; // A is client + nameA = "Client"; + roleB = SERVER; // B is server + nameB = "Server"; + } + else if (protocolSubtype.equals("pp")) { // peer-peer subtype? + roleA = PEER; // A is peer + nameA = "User A"; + roleB = PEER; // B is peer + nameB = "User B"; + } + else if (protocolSubtype.equals("ss")) { // slow start subtype? + roleA = PEER; // A is peer + nameA = "User A"; + roleB = PEER; // B is peer + nameB = "User B"; + } + else // unknown subtype + throw new Exception("unknown protocol subtype '" + protocolSubtype + "'"); + ((TCPMedium) medium).setLossRate(LOSS_RATE);// set medium loss rate + + servA = new TCPService(nameA, roleA); // create service A + servB = new TCPService(nameB, roleB); // create service B + servA.setMessageSize(MESSAGE_SIZE_A); // set service A message size + servB.setMessageSize(MESSAGE_SIZE_B); // set service B message size + if (protocolSubtype.equals("ss")) // slow start? + protA = // create protocol A + new TCPProtocol( + medium, "Protocol A", roleA, INITIAL_SEQUENCE_A, INITIAL_WINDOW_A, + INITIAL_WINDOW_B, INITIAL_SEQUENCE_B); + else // client-server/peer-peer + protA = // create protocol A + new TCPProtocol(medium, "Protocol A", roleA, INITIAL_SEQUENCE_A, + INITIAL_WINDOW_A); + protB = // create protocol B + new TCPProtocol(medium, "Protocol B", roleB, INITIAL_SEQUENCE_B, + INITIAL_WINDOW_B); + servA.setProvider(protA); // service A -> protocol A + servB.setProvider(protB); // service B -> protocol B + protA.setUser(servA); // protocol A -> service A + protA.setPeer(protB); // protocol A -> protocol B + protB.setUser(servB); // protocol B -> service B + protB.setPeer(protA); // protocol B -> protocol A + + entities = new Vector(); // initialise entities list + entities.addElement(servA); // add service A + entities.addElement(protA); // add protocol A + entities.addElement(medium); // add medium + entities.addElement(protB); // add protocol B + entities.addElement(servB); // add service B + } + + /** + Check if the protocol is slow start (i.e. the protocol subtype is for slow + start). + + @return true/false if slow start is/is not in use + */ + public static boolean isSlowStart() { + return( // return check + protocolSubtype.equals("ss")); // slow start subtype + } + + /** + Set a TCP parameter. + + @param parameter parameter + @param value value + */ + public void setParameter(String parameter, String value) { + if (parameter.equals("pushA")) { + boolean push = Boolean.valueOf(value).booleanValue(); + servA.setPush(push); + return; + } + else if (parameter.equals("pushB")) { + boolean push = Boolean.valueOf(value).booleanValue(); + servB.setPush(push); + return; + } + try { + if (parameter.equals("lossRate")) { // loss rate? + float lossRate = Float.valueOf(value).floatValue(); + ((TCPMedium) medium).setLossRate(lossRate); + } + else { // other integer parameter + int size = Integer.parseInt(value); + if (parameter.equals("userAMessageSize")) + servA.setMessageSize(size); + else if (parameter.equals("userBMessageSize")) + servB.setMessageSize(size); + else if (parameter.equals("windowSizeA")) + protA.setWindowSizeDefault(size); + else if (parameter.equals("windowSizeB")) + protB.setWindowSizeDefault(size); + else if (parameter.equals("maxSendPacket")) + TCPProtocol.setSegmentSize(size); + else + System.err.println("unknown parameter '" + parameter + ";"); + } + } + catch (NumberFormatException e) { // invalid numeric value + System.err.println( + "invalid number '" + value + "' for '" + parameter + "'"); + } + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/TCPMedium.java b/lab8_protocols/jasper/source/protocol/TCPMedium.java new file mode 100755 index 0000000..20b5dc2 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TCPMedium.java @@ -0,0 +1,125 @@ +// TCPMedium.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class for a TCP medium. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (26th July 2010, KJT): minor tidying; a loss rate has been + introduced for medium messages +*/ +public class TCPMedium extends Medium { + + /** Message loss rate (0.2 means 20% of messages are lost) */ + private float lossRate = 0.2f; + + /** Random number index */ + private int randomIndex; + + /** Random numbers */ + private Vector randomNumbers; + + /** + Constructor for a TCP medium. + */ + public TCPMedium() { + super(); // construct superclass + randomNumbers = new Vector(); // initialise random numbers + } + + /** + Initialise the medium. + */ + public void initialise() { + super.initialise(); + randomIndex = 0; // initialise random index + } + + /** + Gets the matching PDU. + + @param description PDU description + @return matching PDU + */ + protected PDU getMatchingPDU(String description) { + // identify and return PDU currently on channel with + // type and parameters matching those described in given string + TCPMessage tcpdu; + // try to match PDU type and seq with a PDU currently on channel: + for (Enumeration enumeration = pdus.elements(); + enumeration.hasMoreElements(); ) { + tcpdu = (TCPMessage) enumeration.nextElement(); + if (description.indexOf(tcpdu.getID()) > 0) + return (tcpdu); + } + return (null); // no match found + } + + /** + Return a list of strings describing actions (services) that can be carried + out in the current state. + + @return service descriptions + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + HashSet pdusSent = // initialise list of sources + new HashSet(); + for (Enumeration enumeration = pdus.elements(); // go through PDUs + enumeration.hasMoreElements(); ) { + PDU pdu = (PDU) enumeration.nextElement();// get next PDU + if (pdu != null) { // non-null PDU? + String name = pdu.getSource().getName();// get PDU source name + String description = // get PDU id and name + pdu.getID() + " [" + name + "]"; + if (!pdusSent.contains(name)) { // no PDU with this source? + pdusSent.add(name); // add source to list + list.addElement( // add delivery service + "Deliver " + description + " - no loss"); + } + if (random() < lossRate) // loss is possible? + list.addElement( // add loss service + "Lose " + description + " - congestion/error"); + } + } + return(list); // return service list + } + + /** + Return a random float. If the list of randoms contains an unused value then + use this, otherwise generate a fresh value and add to the list. This allows + random numbers to be generated consistently when simulation steps are + undone/redone. + + @return random number in the range (0..1] + */ + private float random() { + if (randomIndex < randomNumbers.size()) // index within list? + return( // return this random + ((Float)randomNumbers.elementAt(randomIndex++)).floatValue()); + else { // index not within list + Float random = new Float(Math.random()); // get random number + randomNumbers.addElement(random); // add random to list + randomIndex++; // increment list index + return(random.floatValue()); // return rand number + } + } + + /** + Set the medium loss rate. + + @param lossRate loss rate + */ + public void setLossRate(float lossRate) { + this.lossRate = lossRate; // set loss rate + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/TCPMessage.java b/lab8_protocols/jasper/source/protocol/TCPMessage.java new file mode 100755 index 0000000..9c704a1 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TCPMessage.java @@ -0,0 +1,217 @@ +// TCPMessage.java + +package protocol; // protocol package + +import support.*; // import Jasper support classes + +/** + This is the class for a TCP segment. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (26th July 2010, KJT): minor tidying, proper "toString" + method added +*/ +public class TCPMessage extends PDU { + + /** Urgent flag */ + public final static int URG = 32; + + /** Acknowledgement flag */ + public final static int ACK = 16; + + /** Push flag */ + public final static int PSH = 8; + + /** Reset flag */ + public final static int RST = 4; + + /** Synchronise flag */ + public final static int SYN = 2; + + /** Finish flag */ + public final static int FIN = 1; + + /** Acknowledgement sequence number */ + protected int ack = -1; + + /** Flag setting */ + protected int flags = 0; + + /** Window sequence number */ + protected int win = -1; + + /** Protocol flags */ + protected boolean urgFlag, ackFlag, pshFlag, rstFlag, synFlag, finFlag; + + /** + Constructor for a TCP segment. + + @param seq sequence number + @param flags flags + @param size message size + */ + public TCPMessage(int seq, int flags, int size) { + this.seq = seq; + this.flags = flags; + setFlags(); + this.size = size; + } + + /** + Constructor for a TCP segment. + + @param seq sequence number + @param ack acknowlegement sequence number + @param flags flags + @param size message size + */ + public TCPMessage(int seq, int ack, int flags, int size) { + this.seq = seq; + this.ack = ack; + this.flags = flags; + setFlags(); + this.size = size; + } + + /** + Return the label of a TCP segment. + + @return segment label + */ + public String getLabel() { + // return label for arrow representing this segment + // in a time sequence diagram + String label = ""; + if (size > 0) + label += "Seq " + seq + ", "; + if (ackFlag) + label += "Ack " + ack + ", "; + if (win >= 0) + label += "Win " + win; + if (urgFlag) + label += ", URG"; + if (pshFlag) + label += ", PSH"; + if (rstFlag) + label += ", RST"; + if (synFlag) + label += ", SYN"; + if (finFlag) + label += ", FIN"; + return (label); + } + + /** + Return the Acknowledgement attribute of a TCP segment. + + @return Acknowledgement value + */ + public boolean isAck() { + return (ackFlag); + } + + /** + Return the Finish attribute of a TCP segment. + + @return Finish value + */ + public boolean isFin() { + return (finFlag); + } + + /** + Return the Push attribute of a TCP segment. + + @return Push value + */ + public boolean isPsh() { + return (pshFlag); + } + + /** + Return the Reset attribute of a TCP segment. + + @return Reset value + */ + public boolean isRst() { + return (rstFlag); + } + + /** + Return the Synchronise attribute of a TCP segment. + + @return Synchronise value + */ + public boolean isSyn() { + return (synFlag); + } + + /** + Return the Urgent attribute of a TCP segment. + + @return Urgent value + */ + public boolean isUrg() { + return (urgFlag); + } + + /** + Check if the given PDU matches this one. + + @param pdu PDU + @return true/false if PDU does/does not match + */ + public boolean matches(PDU pdu) { + if (pdu == null) + return (false); + if (this.getSource() != pdu.getSource()) // entities must match + return (false); + TCPMessage tcpdu = (TCPMessage) pdu; + // if both segments have valid ACK numbers, check that they match + if (this.isAck() && tcpdu.isAck() && this.ack != tcpdu.ack) + return (false); + return (this.seq == tcpdu.seq && this.flags == tcpdu.flags); + } + + /** + + Set the flags attribute of the TCPMessage object + */ + private void setFlags() { + int f = flags; + finFlag = f % 2 > 0; + f /= 2; + synFlag = f % 2 > 0; + f /= 2; + rstFlag = f % 2 > 0; + f /= 2; + pshFlag = f % 2 > 0; + f /= 2; + ackFlag = f % 2 > 0; + f /= 2; + urgFlag = f % 2 > 0; + } + + /** + Set the window size of a TCP message. + + @param win window size + */ + public void setWindowSize(int win) { + if (win >= 0) + this.win = win; + } + + /** + Convert a PDU to a string representation. + + @return string representation of a PDU + */ + public String toString() { + return ("PDU <" + getLabel() + ">"); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/TCPProtocol.java b/lab8_protocols/jasper/source/protocol/TCPProtocol.java new file mode 100755 index 0000000..e50a010 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TCPProtocol.java @@ -0,0 +1,1230 @@ +// TCPProtocol.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class for a TCP protocol entity. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (26th July 2010, KJT): minor tidying; addition of code to + handle slow starts; empty PDU removed from receive buffer + after reception; state TIME_WAIT now entered after CLOSING or + FIN_WAIT2 and a maximum of two received PDUs allowed in this + state before closing; FIN is now acknowledged in LISTEN state; + a SYN in CLOSING or FIN_WAIT2 state now causes closing +*/ +public class TCPProtocol implements ProtocolEntity, Timeouts { + + /** Debug flag */ + private final static boolean DEBUG = false; + + // Protocol state constants + + /** Closed state */ + private final static int CLOSED = 0; + + /** Listen state */ + private final static int LISTEN = 1; + + /** Synchronise received state */ + private final static int SYN_RCVD = 2; + + /** Synchronise sent state */ + private final static int SYN_SENT = 3; + + /** Synchronise pending at closed peer state */ + private final static int SYN_PEND = 4; + + /** Established state */ + private final static int ESTABLISHED = 5; + + /** Finish waiting 1 state */ + private final static int FIN_WAIT1 = 6; + + /** Finish waiting 2 state */ + private final static int FIN_WAIT2 = 7; + + /** Closing state */ + private final static int CLOSING = 8; + + /** Timed waiting state */ + private final static int TIME_WAIT = 9; + + /** Close waiting state */ + private final static int CLOSE_WAIT = 10; + + /** Closed state */ + private final static int LAST_ACK = 11; + + // TCP segment control flag constants + + /** Urgent flag */ + public final static int URG = TCPMessage.URG; + + /** Urgent flag */ + public final static int ACK = TCPMessage.ACK; + + /** Push flag */ + public final static int PSH = TCPMessage.PSH; + + /** Reset flag */ + public final static int RST = TCPMessage.RST; + + /** Synchronise flag */ + public final static int SYN = TCPMessage.SYN; + + /** Finish flag */ + public final static int FIN = TCPMessage.FIN; + + /** Data flag */ + private final static int DATA = 0; + + // Other protocol constants + + /** Maximum number of retransmission attempts (currently unused) */ + private final static int RETRY_LIMIT = 10; + + /** + Maximum number of PDUs to be received in TIME_WAIT state (the wait ought to + be 2 maximum segment lifetimes, but this is not meaningful as the simulation + does not use time) + */ + private final static int WAIT_LIMIT = 2; + + // Protocol variables + + /** Open confirmed message */ + private final static String OPEN_CONFIRMED = TCPService.OPEN_CONFIRMED; + + /** Send message */ + private final static String SEND = "Send"; + + /** Deliver message */ + private final static String DELIVER = "Deliver"; + + /** Resend message */ + private final static String RESEND = "Timeout - resend"; + + /** Maximum medium size */ + private static int segmentSize; + + /** Peer protocol entity */ + private ProtocolEntity peer; + + /** Peer protocol entity */ + private TCPService user; + + /** Peer protocol entity */ + private Medium medium; + + /** Peer protocol entity */ + private String name; + + /** Events from entity */ + private Vector events; + + /** Events from user */ + private Vector userEvents; + + /** Segments for timeout */ + private Vector timedSegments; + + /** Incoming segments */ + private Vector recvBuffer; + + /** Segments for sending */ + private Vector sendBuffer; + + /** Peer protocol entity */ + private int role; + + /** Current state */ + private int state; + + /** Previous state */ + private int prevState = -1; + + /** Next send sequence number */ + private int sendSeq; + + /** Acknowledgement sequence number to be sent */ + private int sendAck; + + /** Next expected sequence number */ + private int recvSeq; + + /** Acknowledgement sequence number received */ + private int recvAck; + + /** Local receive window */ + private int recvWindow; + + /** Peer receive window */ + private int peerWindow; + + /** Original peer initial receive window */ + private int peerWindowOriginal; + + /** Original peer initial receive sequence number */ + private int peerSequenceOriginal; + + /** Initial sequence number */ + private int initSeq; + + /** Local window size */ + private int window; + + /** Congestion window size */ + private int congestionWindow; + + /** Slow start threshold */ + private int slowStartThreshold; + + /** Start sequence number */ + private int deliverableSeq; + + /** Size of data */ + private int deliverableSize; + + /** Whether protocol requests have previously been made */ + private boolean laterRequest; + + /** Number of PDUs received in TIME_WAIT state */ + private int waitCount; + + /** Number of octets sent but not acknowledged */ + private int sentPending; + + /** Retry count (currently unused) */ + private int retryCount; + + /** + Constructor for a TCP protocol entity. + + @param medium protocol medium + @param name protocol name + @param role protocol role + @param initSeq initial sequence number + @param window initial window + */ + public TCPProtocol(Medium medium, String name, int role, int initSeq, + int window) { + this.name = name; + this.medium = medium; + this.role = role; + this.initSeq = initSeq; + this.window = window; + initialise(); + } + + /** + Constructor for a TCP protocol entity (slow start subtype). + + @param medium protocol medium + @param name protocol name + @param role protocol role + @param initSeq initial sequence number + @param window initial window + @param peerWindow initial peer window + @param peerSequence initial peer sequence number + */ + public TCPProtocol(Medium medium, String name, int role, int initSeq, + int window, int peerWindow, int peerSequence) { + this.name = name; + this.medium = medium; + this.role = role; + this.initSeq = initSeq; + this.window = window; + peerWindowOriginal = peerWindow; + peerSequenceOriginal = peerSequence; + initialise(); + } + + /** + Cancel retransmissions. + + @param pdu PDU whose retranmission is to be cancelled + */ + private void cancelRetransmissions(TCPMessage pdu) { + if (pdu != null) { + int una = oldestUnacked(); // get oldest unack'ed + int ack = pdu.ack; // get ack seq no + if (una >= 0 && una < ack && ack <= sendSeq) { // ack OK + recvAck = ack; // note ack seq no + // identify TCP segment(s) being acknowledged + for (Enumeration enumeration = timedSegments.elements(); + enumeration.hasMoreElements(); ) { + TCPMessage tcpdu = (TCPMessage) enumeration.nextElement(); + if (tcpdu.seq + tcpdu.size <= ack) { + // segment fully acknowledged, remove from retransmission queue + timedSegments.removeElement(tcpdu); + sentPending -= tcpdu.size; // reduce amount of sent pending + enumeration = timedSegments.elements(); + } + } + } + } + } + + /** + Close the protocol by setting CLOSE state and re-initialising medium. + */ + public void closeProtocol() { + setState(CLOSED); // set state closed + } + + /** + Return a protocol event with the given comment. + + @param comment protocol comment + @return protocol event + */ + public ProtocolEvent comment(String comment) { + return(new ProtocolEvent(ProtocolEvent.COMMENT, this, comment)); + } + + /** + If this is protocol A, add to global protocol events (events) a + comment with the given congestion comment. + + @return protocol event + */ + public void commentCongestion() { + if (name.equals("Protocol A")) { // protocol A? + String windowIncrease = // get window increase type + congestionWindow < slowStartThreshold ? "exp." : "lin."; + events.addElement( // add congestion comment + comment("cwind " + congestionWindow + " (" + windowIncrease + ")")); + } + } + + /** + Return the protocol name. + + @return The name value + */ + public String getName() { + return(name); + } + + /** + Return protocol entity services. + + @return protocol entity services + */ + public Vector getServices() { + Vector services = new Vector(); + int size = 0; + if (!sendBuffer.isEmpty()) { // send buffer non-empty? + int firstSize = // get first send element size + ((PDU) sendBuffer.firstElement()).size; + int protocolWindow = getWindow(); // get window + size = Math.min(firstSize, protocolWindow); + if (protocolWindow > 0) { // can send to peer? + services.addElement(SEND + " " + size + " octets to peer"); + } + } + if (!recvBuffer.isEmpty()) { // receive buffer non-empty? + sortBuffer(); // sort into ascending seq. nos. + // check first sequence number in buffer is one we expect + PDU pdu = (PDU) recvBuffer.firstElement(); + int firstSeq = pdu.seq; + if (firstSeq <= recvSeq) { // check contiguous data + int seq = firstSeq; + for (Enumeration enumeration = recvBuffer.elements(); + enumeration.hasMoreElements(); ) { + pdu = (PDU) enumeration.nextElement(); + if (pdu.seq != seq) + break; + else + seq += pdu.size; + } + size = seq - firstSeq; + if (size > 0) + // offer to deliver buffered data to user + services.addElement(DELIVER + " " + size + " octets to user"); + deliverableSeq = firstSeq; + deliverableSize = size; + } + } + // check for oldest segment that could have timed out + for (Enumeration enumeration = timedSegments.elements(); + enumeration.hasMoreElements(); ) { + TCPMessage tcpdu = (TCPMessage) enumeration.nextElement(); + if (tcpdu.seq == oldestUnacked()) + services.addElement(RESEND + " " + tcpdu.getID()); + } + return(services); + } + + /** + Extract integer value from string s with prefix p, removing prefix p + (including space character at end). + + @param s string + @param p prefix + @return extracted integer + */ + private int getSize(String s, String p) { + String s1 = s.substring(p.length() + 1); + return(Integer.parseInt(s1.substring(0, s1.indexOf(' ')))); + } + + /** + Return the available window. For client-server or peer-peer, this is the + peer window. For slow start, this is the minimum of the peer window and the + congestion window (less what has been sent but is outstanding). + + @return available window + */ + private int getWindow() { + int availableWindow = // get available window + Math.min(congestionWindow - sentPending, peerWindow); + return(availableWindow); // return window + } + + /** + Return whether the protocol uses a timer. All TCP segments are of same type; + the need for a timer depends on whether segment contains data. + + @param type type description + @return true/false if protocol uses timer (always true) + */ + public boolean hasTimer(String type) { + return(true); + } + + /** + Initialise the protocol. + */ + public void initialise() { + events = new Vector(); // empty events list + userEvents = new Vector(); // empty user events list + timedSegments = new Vector(); // empty unack'ed segments list + sendBuffer = new Vector(); // empty sent segments + recvBuffer = new Vector(); // empty received segments + recvWindow = window; // init received window size + sendSeq = initSeq; // init send ack seq no + recvAck = -1; // init received ack seq no + waitCount = 0; // initialise waiting PDU count + sentPending = 0; // initialise sent pending count + retryCount = 0; // initialise retry count + if (TCP.isSlowStart()) { // slow start? + setState(ESTABLISHED); // start directly as established + congestionWindow = segmentSize; // initialise congestion window + slowStartThreshold = Integer.MAX_VALUE; // initialise threshold + laterRequest = false; // initialise not later request + peerWindow = peerWindowOriginal; // initialise peer window + recvSeq = peerSequenceOriginal; // initialise peer seq. no. + } + else { // client-server/peer-peer + setState(CLOSED); // start as closed + congestionWindow = Integer.MAX_VALUE; // set no congestion window + slowStartThreshold = Integer.MAX_VALUE; // set no threshold + } + } + + /** + Check if a sequence number is a duplicate. + + @param seq sequence number + @return true/false if sequence number is/is not a duplicate + */ + private boolean isDuplicate(int seq) { + // return true if receive buffer already contains + // a segment with sequence number seq + if (recvBuffer.isEmpty()) + return(false); + for (Enumeration enumeration = recvBuffer.elements(); + enumeration.hasMoreElements(); ) { + PDU pdu = (PDU) enumeration.nextElement(); + if (pdu.seq == seq) { + return(true); + } + } + return(false); + } + + /** + Check if the sequence number is within the window. + + @param seq sequence number + @return true/false if sequence number is/is not within window + */ + private boolean isWithinWindow(int seq) { + return(recvSeq <= seq && seq < recvSeq + window); + } + + /** + Return oldest unacknowledged sequence number. + + @return Return Value + */ + private int oldestUnacked() { + if (timedSegments.isEmpty()) // no unacknowledged segments + return(-1); + int oldestSoFar = ((PDU) timedSegments.firstElement()).seq; + for (Enumeration e = timedSegments.elements(); e.hasMoreElements(); ) { + PDU pdu = (PDU) e.nextElement(); + if (pdu.seq < oldestSoFar) + oldestSoFar = pdu.seq; + } + return(oldestSoFar); + } + + /** + Perform given service. + + @param service service + @return resulting protocol events + */ + public Vector performService(String service) { + events.removeAllElements(); // remove previous prot. events + userEvents.removeAllElements(); // remove previous user events + if (service.startsWith(SEND)) { // send message to peer? + // send buffered data to peer + int size = getSize(service, SEND); + if (sendBuffer.size() > 0) { // at least one element? + TCPMessage tcpdu = (TCPMessage) sendBuffer.firstElement(); + sendToPeer(tcpdu.flags, size); + if (tcpdu.size > size) + tcpdu.size -= size; + else + sendBuffer.removeElement(tcpdu); + } + else // send buffer is empty + System.err.println("cannot send as send buffer is empty"); + } + else if (service.startsWith(DELIVER)) { // deliver message to user? + // deliver buffered data to user + int size = deliverableSize; + int maxSeq = deliverableSeq + size; + int lastElement = 0; // first PDU to stay + for (Enumeration enumeration = recvBuffer.elements(); + enumeration.hasMoreElements(); ) { + PDU pdu = (PDU) enumeration.nextElement(); + if (pdu.seq > maxSeq) + break; + else + lastElement++; + } + while (lastElement-- > 0) + recvBuffer.remove(0); + recvWindow += size; // alter receive window + sendToUser(TCPService.DELIVER + " (" + size + ")"); + deliverableSize = 0; + sendToPeer(0); // open up window + } + else if (service.startsWith(RESEND)) { // re-send on timeout? + if (TCP.isSlowStart()) { // slow start? + slowStartThreshold = // reset slow start theshold + Math.max((int) (congestionWindow / 2), segmentSize); + events.addElement( // add threshold comment + comment("ssthresh " + slowStartThreshold)); + congestionWindow = segmentSize; // reset congestion window + commentCongestion(); // add congestion window comment + } + // identify segment to be retransmitted + String label = service.substring(RESEND.length() + 1); + for (Enumeration enumeration = timedSegments.elements(); + enumeration.hasMoreElements(); ) { + TCPMessage tcpdu = (TCPMessage) enumeration.nextElement(); + if (label.equals(tcpdu.getLabel())) { + transmitPDU(tcpdu, peer); + timedSegments.removeElement(tcpdu); + events.addElement(new ProtocolEvent(ProtocolEvent.TIMEOUT, tcpdu)); + break; + } + } + } + for (Enumeration enumeration = userEvents.elements(); + enumeration.hasMoreElements(); ) + events.addElement((ProtocolEvent) enumeration.nextElement()); + return(events); + } + + /** + Check for data waiting in the send buffer with the push flag set. + */ + private void pushData() { + if (!sendBuffer.isEmpty()) { + TCPMessage tcpdu = (TCPMessage) sendBuffer.firstElement(); + int protocolWindow = getWindow(); // get window + int size = Math.min(tcpdu.size, protocolWindow); + if (tcpdu.isPsh()) { + sendToPeer(tcpdu.flags, size); + if (tcpdu.size > size) + tcpdu.size -= size; + else + sendBuffer.removeElement(tcpdu); + } + } + } + + /** + Check that data segment from peer is within receive window and is not a + duplicate of a segment already received. + + @param pdu PDU + */ + private void receiveData(PDU pdu) { + int seq = pdu.seq; + int size = pdu.size; + if ((isWithinWindow(seq) || + isWithinWindow(seq + size - 1)) && + !isDuplicate(seq)) { + recvBuffer.addElement(pdu); // store PDU in receive buffer + recvWindow -= size; + sortBuffer(); // sort buffer + int firstSeq = ((PDU) recvBuffer.firstElement()).seq; + if (firstSeq <= recvSeq) { + deliverableSeq = firstSeq; + for (Enumeration enumeration = recvBuffer.elements(); + enumeration.hasMoreElements(); ) { + pdu = (PDU) enumeration.nextElement(); + if (pdu.seq > deliverableSeq) + break; + else if (pdu.seq == deliverableSeq) + deliverableSeq += pdu.size; + } + deliverableSize = deliverableSeq - firstSeq; + } + else { + deliverableSeq = recvSeq; + deliverableSize = 0; + } + sendAck = deliverableSeq; // update ack seq no + recvSeq = deliverableSeq; // update exp. seq no + recvWindow = window - deliverableSize; // update receive window + sendToPeer(ACK); // respond with ACK + if (size == 0) // PDU contains no data? + recvBuffer.removeElement(pdu); // remove from receive buffer + // must call getServices to ensure a buffer check when undoing + getServices(); // check buffers + } + else { // acknowledge duplicate + events.addElement(comment("ignored")); // add ignored comment + // sendAck = pdu.seq + pdu.size; + sendToPeer(ACK); // respond with ACK + } + } + + /** + Receive a PDU/SDU + + @param pdu PDU/SDU + @return resulting protocol events + */ + public Vector receivePDU(PDU pdu) { + events.removeAllElements(); // remove previous prot. events + userEvents.removeAllElements(); // remove previous user events + if (pdu == null) + return(events); + String pduType = pdu.type; + int flags = -1; + TCPMessage tcpduRecv = null; + if (pdu instanceof TCPMessage) { + tcpduRecv = (TCPMessage) pdu; + flags = tcpduRecv.flags; + peerWindow = tcpduRecv.win; + } + switch (state) { + + case CLOSED: // closed? + if (pduType.equals(TCPService.ACTIVE_OPEN)) { + // step 1 of three-way handshake + sendToPeer(SYN); + setState(SYN_SENT); + } + else if (pduType.equals(TCPService.PASSIVE_OPEN)) + setState(LISTEN); + else if (flags == SYN && role == TCP.PEER) { // closed peer gets SYN? + recvSeq = pdu.seq + pdu.size; // note next seq no + sendAck = recvSeq; // note seq no for ACK + events.addElement(comment("open queued")); // add open queued comment + setState(SYN_PEND); // note SYN pending + } + else if ((flags & RST) != 0) // reset? + timedSegments = new Vector(); // cancel retransmissions + else // stray segment + reset(flags, pdu); // reset connection + break; + + case LISTEN: // listening after passive open? + if (flags == SYN) { + // step 2 of three-way handshake + recvSeq = pdu.seq + pdu.size; + sendAck = recvSeq; + sendToPeer(SYN + ACK); + sendToUser(TCPService.OPEN_RECEIVED); + prevState = LISTEN; + setState(SYN_RCVD); + } + else if (flags == FIN) { + sendAck = pdu.seq + 1; + sendToPeer(ACK); + } + else if (pduType.equals(TCPService.CLOSE)) + closeProtocol(); + else if (pduType.equals(TCPService.SEND)) { + sendToPeer(SYN); + setState(SYN_SENT); + } + else { + events.addElement(comment("ignored")); // add ignored comment + reset(flags, pdu); // reset connection + } + break; + + case SYN_SENT: // SYN sent? + if (flags == SYN + ACK) { + // step 3 of three-way handshake + cancelRetransmissions(tcpduRecv); + recvSeq = pdu.seq + pdu.size; + sendAck = recvSeq; + sendToPeer(ACK); + events.addElement(comment("established")); // add established comment + sendToUser(TCPService.OPEN_SUCCESS); + setState(ESTABLISHED); + } + else if (flags == SYN) { // SYN? + recvSeq = pdu.seq + pdu.size; // get next seq no + sendAck = recvSeq; // set ack seq no + sendSeq--; // reset send seq no + sendToPeer(SYN + ACK); // SYN and ACK + prevState = SYN_SENT; // previously SYN sent + setState(SYN_RCVD); // now SYN received + } + else if ((flags & RST) != 0) { // reset by peer? + sendToUser(TCPService.OPEN_FAILURE); // report rejected + timedSegments = new Vector(); // cancel retrans + closeProtocol(); // now closed + } + else if ((flags & FIN) != 0) { // finish by peer? + sendToUser(TCPService.OPEN_FAILURE); // report rejected + timedSegments = new Vector(); // cancel retrans + reset(flags, pdu); // reset connection + closeProtocol(); // now closed + } + else if (pduType.equals(TCPService.CLOSE)) // user close? + closeProtocol(); // now closed + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case SYN_RCVD: // SYN received? + if (flags == ACK || flags == SYN + ACK) { + // cancel timer on previously sent SYN + ACK segment: + cancelRetransmissions(tcpduRecv); + events.addElement(comment("established")); // add established comment + if (prevState == LISTEN) // passive open confirm? + transmitPDU(new PDU(OPEN_CONFIRMED), user); + if (prevState == SYN_SENT) // was SYN sent? + sendToUser(TCPService.OPEN_SUCCESS); + + if (prevState == SYN_PEND) // was SYN pending? + sendToUser(TCPService.OPEN_SUCCESS); + setState(ESTABLISHED); + } + else if ((flags & FIN) != 0) { // other user closed? + sendToUser(TCPService.OPEN_FAILURE); // report open failure + timedSegments = new Vector(); // cancel retrans + recvSeq = pdu.seq + pdu.size; // set next seq expected + sendAck = recvSeq; // set ack seq no + sendToPeer(FIN + ACK); // acknowledge FIN + prevState = SYN_RCVD; // was SYN received + setState(LAST_ACK); // waiting for FIN ack + } + else if (pduType.equals(TCPService.CLOSE)) { + sendToPeer(FIN); + setState(FIN_WAIT1); // wait for FIN ack + } + else if (flags == SYN) { + // duplicate SYN segment (due to retransmission) + timedSegments.removeAllElements(); + sendSeq--; // restore seq no + peerWindow++; // restore peer window + sendToPeer(SYN + ACK); // resend SYN + ACK + } + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case SYN_PEND: // SYN for closed peer? + if (pduType.equals(TCPService.ACTIVE_OPEN)) { // active open? + sendToPeer(SYN + ACK); // SYN, ACK for prev SYN + prevState = SYN_PEND; // was SYN pending state + setState(SYN_RCVD); // now SYN rcvd. state + } + else if ((flags & RST) != 0) // reset? + closeProtocol(); // back to closed + else // not active open/reset + events.addElement(comment("ignored"));// add ignored comment + break; + + case ESTABLISHED: // established? + int seq = pdu.seq; // seq. no. first octet + int size = pdu.size; // received lengt + if (pduType.startsWith(TCPService.SEND)) { // user data request + if (TCP.isSlowStart()) { // slow start? + if (!laterRequest) { // first request? + laterRequest = true; // note now later request + commentCongestion(); // add congestion comment + } + } + transmitData(pduType, size); + break; + } + else if (pduType.equals(TCPService.CLOSE)) { // user close + sendToPeer(FIN); + prevState = ESTABLISHED; + setState(FIN_WAIT1); + break; + } + if (tcpduRecv == null) // null PDU received? + break; // just in case + else if (flags == DATA || // DATA received or + (flags == DATA + ACK && size > 0)) { // DATA and ACK received? + if ((flags & ACK) != 0) // ACK? + updateWindow(); // update congestion window + receiveData(pdu); + } + else if (tcpduRecv.isPsh()) { // push data to user at once + if (isWithinWindow(seq) || isWithinWindow(seq + size - 1)) { + recvBuffer.addElement(pdu); // buffer to user, ack + sortBuffer(); + seq = ((PDU) recvBuffer.firstElement()).seq; + PDU lastPDU = (PDU) recvBuffer.lastElement(); + size = lastPDU.seq + lastPDU.size - seq; + sendAck = seq + size; + recvSeq += size; // update exp seq no + recvBuffer.removeAllElements(); + recvWindow = window; + sendToPeer(ACK); // send ACK to peer + sendToUser(TCPService.DELIVER + " (" + size + ")"); + } + else { // ack duplicate + sendAck = pdu.seq + pdu.size; + sendToPeer(ACK); + } + } + else if (flags == SYN + ACK) { // duplicate SYN with ACK? + cancelRetransmissions(tcpduRecv); // ack from peer + sendAck = pdu.seq + pdu.size; // get next ack seq no + sendToPeer(ACK); // acknowledge again + } + else if (flags == FIN) { // FIN received? + sendAck = pdu.seq + 1; + sendToPeer(ACK); + sendToUser(TCPService.CLOSING); + setState(CLOSE_WAIT); + } + else if ((flags & RST) != 0) { // RST received? + if (TCP.isSlowStart()) // slow start? + initialise(); // re-initialise protocol + else // client-server/peer-peer + ; // *** what should happen here? + } + else if ((flags & ACK) != 0) { // ACK received? + updateWindow(); // update congestion window + cancelRetransmissions(tcpduRecv); // ack from peer + pushData(); // push data to user + int oldest = oldestUnacked(); // get oldest unacknowledged + if (oldest != -1) { // some data pending? + int pending = sendSeq - oldest; // get number of octets pending + peerWindow -= pending; // reduce window by pending + } + } + break; + + case FIN_WAIT1: // FIN sent? + if (flags == DATA || (flags == DATA + ACK && pdu.size > 0)) + receiveData(pdu); + else if (flags == ACK) { + cancelRetransmissions(tcpduRecv); + setState(FIN_WAIT2); + } + else if ((flags & SYN) != 0) // duplicate SYN? + sendToPeer(ACK); // acknowledge it + else if (flags == FIN && // FIN? + pdu.seq == recvSeq) { // expected seq no? + sendAck = pdu.seq + 1; // get seq no to ack + sendToPeer(ACK); // acknowledge FIN + setState(CLOSING); // now closing + } + else if (flags == FIN + ACK) { // FIN and ACK? + sendAck = pdu.seq + 1; // get seq no to ack + cancelRetransmissions(tcpduRecv); // cancel retrans + sendToPeer(ACK); // acknowledge FIN + if (prevState == ESTABLISHED) // was established? + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); // now closed + } + else if ((flags & RST) != 0) { // reset? + cancelRetransmissions(tcpduRecv); // cancel retrans + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); + } + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case FIN_WAIT2: // FIN received? + if (flags == DATA) // DATA? + receiveData(pdu); + else if (flags == ACK) { // ACK? + cancelRetransmissions(tcpduRecv); // cancel retrans + setState(FIN_WAIT2); + } + else if (flags == SYN + ACK) // SYN and ACK? + // duplicate segment due to retransmission + sendToPeer(ACK); + else if (flags == FIN) { // FIN? + if (sendAck == pdu.seq) { // all segments sent? + sendAck = pdu.seq + 1; + if (deliverableSize > 0) // data to deliver? + sendToUser(TCPService.DELIVER + " (" + deliverableSize + ")"); + recvWindow = window; // open receive window + recvBuffer.removeAllElements(); // empty receive buffer + sendToPeer(ACK); // send ack + sendToUser(TCPService.CLOSED); // notify user closed + closeProtocol(); // note state closed + } + else { // outstanding segments + sendToPeer(ACK); // send ack + setState(TIME_WAIT); // set time wait state + // sendToUser(TCPService.CLOSED); + // closeProtocol(); // following buffered + } + } + else if ((flags & RST) != 0 || // reset or + (flags & SYN) != 0) { // synchronise? + cancelRetransmissions(tcpduRecv); // cancel retrans + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); + } + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case CLOSING: // closing? + if (flags == ACK || // finish ack or + (flags & RST) != 0) { // reset? + cancelRetransmissions(tcpduRecv); // cancel retrans + if (tcpduRecv.ack == sendSeq) { // all segments acknowledged? + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); // set closed state + } + else // enter final wait + setState(TIME_WAIT); // set time wait state + } + else if ((flags & RST) != 0 || // reset or + (flags & SYN) != 0) { // synchronise? + cancelRetransmissions(tcpduRecv); // cancel retransmission + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); + } + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case CLOSE_WAIT: // waiting to close? + if (pduType.equals(TCPService.CLOSE)) { + sendToPeer(FIN); + setState(LAST_ACK); + } + if (pduType.startsWith(TCPService.SEND)) // user data request + transmitData(pduType, pdu.size); + + if (tcpduRecv != null && tcpduRecv.isAck()) { + // acknowledgement from peer + cancelRetransmissions(tcpduRecv); // cancel retrans + pushData(); + } + if (flags == DATA || (flags == DATA + ACK && pdu.size > 0)) + receiveData(pdu); + + if (flags == FIN) { + sendAck = pdu.seq + 1; + sendToPeer(ACK); + } + break; + + case LAST_ACK: // last ACK expected? + if (flags == -1) // primitive (open)? + // sendToUser(TCPService.CLOSED); // report closed + // closeProtocol(); + ; + else if (flags == ACK) { // ACK? + cancelRetransmissions(tcpduRecv); // cancel retrans + if (timedSegments.size() == 0) { // queue now empty? + // prevState != SYN_RCVD) { // was not SYN received? + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); // now closed + } + } + else if (flags == FIN) { // FIN? + sendAck = pdu.seq + 1; + sendToPeer(ACK); + } + else if (flags == SYN) { // new SYN from peer? + sendToUser(TCPService.CLOSED); // report rejected + timedSegments = new Vector(); // cancel retrans + reset(flags, pdu); // reset connection + closeProtocol(); // set closed + } + else if ((flags & RST) != 0) { // reset? + cancelRetransmissions(tcpduRecv); // cancel retrans + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); // set closed + } + else + events.addElement(comment("ignored"));// add ignored comment + break; + + case TIME_WAIT: // final timed waiting? + waitCount++; // increment wait count + if (waitCount >= WAIT_LIMIT) { // wait limit reached? + cancelRetransmissions(tcpduRecv); // cancel retrans + sendToUser(TCPService.CLOSED); // report closed + closeProtocol(); // set closed + medium.initialise(); // re-initialise medium + } + } + for (Enumeration enumeration = userEvents.elements(); + enumeration.hasMoreElements(); ) + events.addElement((ProtocolEvent) enumeration.nextElement()); + return(events); + } // end of receivePDU + + /** + Handle Reset. + + @param flags protocol flags + @param pdu PDU + */ + private void reset(int flags, PDU pdu) { // reset connection + if ((flags & ACK) != 0) { // ACK flag set? + sendSeq = recvSeq; // use incoming seq no + sendToPeer(RST); // reset peer + } + else { // ACK flag unset + sendSeq = 0; // default 0 seq no + recvSeq = pdu.seq + pdu.size; // note next seq no + sendAck = recvSeq; // note seq no for ACK + sendToPeer(RST + ACK); // reset peer + } + } + + /** + Send message to peer with the specified flags. + + @param flags protocol flags + */ + private void sendToPeer(int flags) { + if (flags == 0) { // pure window change + TCPMessage tcpdu = new TCPMessage(sendSeq, sendAck, 0, 0); + tcpdu.setWindowSize(recvWindow); + transmitPDU(tcpdu, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, tcpdu)); + } + else if (flags == ACK) { // pure ACK - no seq. + TCPMessage tcpdu = new TCPMessage(sendSeq, sendAck, ACK, 0); + tcpdu.setWindowSize(recvWindow); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, tcpdu)); + transmitPDU(tcpdu, peer); + } + else + sendToPeer(flags, 1); // other - seq. 1 + } + + /** + Send message to peer with the specified flags and packet size. + + @param flags protocol flags + @param packetSize packet size + */ + private void sendToPeer(int flags, int packetSize) { + // send packet of given size to peer entity with specified flags set + // if packet size too large for medium to handle, fragment it + for (int p = 0; p < packetSize; p += segmentSize) { + int size = Math.min(packetSize - p, segmentSize); + TCPMessage tcpdu = new TCPMessage(sendSeq, sendAck, flags, size); + if (tcpdu.isAck()) + flags -= ACK; // ACK only in first + tcpdu.setWindowSize(recvWindow); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, tcpdu)); + transmitPDU(tcpdu, peer); + sendSeq += size; + peerWindow -= size; + sentPending += size; // increase pending sent data + } + } + + /** + Send message to user. + + @param type SDU type + */ + private void sendToUser(String type) { + PDU pdu = new PDU(type); + transmitPDU(pdu, user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pdu)); + } + + /** + Set protocol peer. + + @param peer protocol peer + */ + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + /** + Set segment size (maximum send packet size). + + @param size maximum send packet size + */ + public static void setSegmentSize(int size) { + segmentSize = size; + } + + /** + Set the protocol state. + + @param state protocol state + */ + public void setState(int state) { + if (DEBUG) + System.err.println("state (" + name + "): " + state); + this.state = state; + } + + /** + Sets the timer for a PDU. + + @param pdu PDU + @param enabled whether timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { + if (enabled && pdu.size > 0 && // enabled, non-empty? + pdu.seq >= recvAck && // new segment? + !((TCPMessage) pdu).isRst()) // no reset flag? + timedSegments.addElement(pdu); + } + + /** + Set the protocol user. + + @param user protocol user (service entity) + */ + public void setUser(ProtocolEntity user) { + this.user = (TCPService) user; + } + + /** + Set the protocol window size. + + @param winSize window size + */ + public void setWindowSize(int winSize) { + recvWindow = winSize; + } + + /** + Set the protocol default window size. + + @param winSize default window size + */ + public void setWindowSizeDefault(int winSize) { + window = winSize; + recvWindow = winSize; + } + + /** Description of the Method */ + private void sortBuffer() { + PDU pdu1; + PDU pdu2; + if (recvBuffer.isEmpty()) + return; + int size = recvBuffer.size(); // sort buffer in order + if (size < 2) + return; // no need to sort + for (int i = 0; i < size; i++) + for (int j = i + 1; j < size; j++) { + pdu1 = (PDU) recvBuffer.elementAt(i); + pdu2 = (PDU) recvBuffer.elementAt(j); + if (pdu2.seq < pdu1.seq) { + recvBuffer.setElementAt(pdu2, i); + recvBuffer.setElementAt(pdu1, j); + } + } + + } + + /** + Transmit protocol data. + + @param pduType PDU type + @param size PDU size + */ + private void transmitData(String pduType, int size) { + // check whether PUSH flag is to be set + int sendFlags = pduType.indexOf(TCPService.PUSH) > 0 ? PSH : DATA; + int protocolWindow = getWindow(); // get window + if (size <= protocolWindow) { // can send all the data? + if (deliverableSize > 0) { // piggyback ACK on data + sendAck = deliverableSeq + deliverableSize; + sendFlags += ACK; + } + sendToPeer(sendFlags, size); + } + else { // can send only some + // buffer data to be sent when peer's receive buffer has space + if (protocolWindow > 0) { + size -= protocolWindow; // send as much as possible + sendToPeer(sendFlags, protocolWindow); + } + // ... and buffer the rest + TCPMessage tcpdu = new TCPMessage(sendSeq, sendAck, sendFlags, size); + tcpdu.setWindowSize(recvWindow); + sendBuffer.addElement(tcpdu); + events.addElement(comment("buffered")); // add buffered comment + } + } + + /** + Transmit PDU. + + @param pdu PDU + @param destination destination + */ + public void transmitPDU(PDU pdu, ProtocolEntity destination) { + pdu.setSource(this); // set PDU source + pdu.setDestination(destination); // set PDU destination + if (destination == peer) { // sending to peer? + userEvents = medium.receivePDU(pdu); + } + else // sending to service + userEvents = user.receivePDU(pdu); + } + + /** + Update congestion window on ACK if window management subtype. + */ + public void updateWindow() { + if (TCP.isSlowStart()) { // slow start? + congestionWindow += segmentSize; // augment congestion window + commentCongestion(); // add congestion window comment + } + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/TCPService.java b/lab8_protocols/jasper/source/protocol/TCPService.java new file mode 100755 index 0000000..64ddebb --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TCPService.java @@ -0,0 +1,285 @@ +// TCPService.java + +package protocol; // protocol package + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class for a TCP service entity. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (26th July 2010, KJT): minor tidying +*/ +public class TCPService implements ProtocolEntity { + + /** Debug flag */ + private final static boolean DEBUG = false; + + // Service primitives (based on Halsall Figs 11.6 - 11.8): + + /** Active open message */ + public final static String ACTIVE_OPEN = "Active Open"; + + /** Passive open message */ + public final static String PASSIVE_OPEN = "Passive Open"; + + /** Open failure message */ + public final static String OPEN_FAILURE = "Open Failure"; + + /** Open received message */ + public final static String OPEN_RECEIVED = "Open Received"; + + /** Open success message */ + public final static String OPEN_SUCCESS = "Open Success"; + + /** Send message */ + public final static String SEND = "Send"; + + /** Deliver message */ + public final static String DELIVER = "Deliver"; + + /** Close message */ + public final static String CLOSE = "Close"; + + /** Closing message */ + public final static String CLOSING = "Closing"; + + /** Closed message */ + public final static String CLOSED = "Closed"; + + /** Abort message */ + public final static String ABORT = "Abort"; + + /** Push message */ + public final static String PUSH = "(Push)"; + + /** Open confirmed message (internal use) */ + public final static String OPEN_CONFIRMED = "Open Confirmed"; + + // State constants + + /** Connected state */ + private final static int CONNECTED = 0; + + /** Calling state */ + private final static int CALLING = 1; + + /** Listening state */ + private final static int LISTENING = 2; + + /** Close sent state */ + private final static int CLOSE_SENT = 3; + + /** Disconnected state */ + private final static int DISCONNECTED = 4; + + /** Protocol for service */ + private ProtocolEntity provider; + + + /** Events from provider */ + private Vector providerEvents; + + /** Service name */ + private String name; + + /** Service entity role */ + private int role; + + /** Protocol state */ + private int state; + + /** Request Push flag */ + private boolean push; + + /** Message size */ + private int messageSize; + + /** + Constructor for a TCP service entity. + + @param name service name + @param role service role + */ + public TCPService(String name, int role) { + this.name = name; + this.role = role; + initialise(); + } + + /** + Return the TCP service entity name. + + @return service name + */ + public String getName() { + return (name); + } + + /** + Return the services offered by the entity. + + @return services offered + */ + public Vector getServices() { + Vector services = new Vector(); + if (state == CONNECTED) { // connected? + if (!TCP.isSlowStart() || // not slow start and + name.equals("User A")) { // user A? + String service = // set send service + SEND + " " + messageSize + " octets"; + if (push) // push flag set? + service += " " + PUSH; // append push + services.addElement(service); // add send service + } + if (!TCP.isSlowStart()) // not slow start? + services.addElement(CLOSE); // add close service + } + else if (state == DISCONNECTED) { + if (role == TCP.SERVER) + services.addElement(PASSIVE_OPEN); + else + services.addElement(ACTIVE_OPEN); + } + return (services); + } + + /** + Initialise service entity. + */ + public void initialise() { + providerEvents = new Vector(); + push = false; + if (TCP.isSlowStart()) // slow start? + setState(CONNECTED); // start directly as connected + else // client-server/peer-peer + setState(DISCONNECTED); // start as disconnected + } + + /** + Perform entity service. + + @param service service requested + @return resulting service events + */ + public Vector performService(String service) { + Vector events = new Vector(); + PDU pdu; + switch (state) { + case CONNECTED: + if (service.startsWith(SEND)) { + String type = SEND + " (" + messageSize + ")"; + if (service.indexOf(PUSH) > 0) + type += " " + PUSH; + pdu = new PDU(type); + pdu.size = messageSize; + transmitPDU(pdu, provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + } + if (service.startsWith(CLOSE)) { + pdu = new PDU(CLOSE); + transmitPDU(pdu, provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + setState(CLOSE_SENT); + } + break; + case CALLING: + break; + case DISCONNECTED: + if (service.equals(ACTIVE_OPEN)) { + pdu = new PDU(service); + transmitPDU(pdu, provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + setState(CALLING); + } + if (service.equals(PASSIVE_OPEN)) { + pdu = new PDU(service); + transmitPDU(pdu, provider); + events.addElement( + new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu)); + setState(LISTENING); + } + break; + } + for (Enumeration enumeration = providerEvents.elements(); + enumeration.hasMoreElements(); ) + events.addElement((ProtocolEvent) enumeration.nextElement()); + return (events); + } + + /** + Receive SDU. + + @param sdu SDU + @return resulting service events + */ + public Vector receivePDU(PDU sdu) { + // respond to PDU received from underlying protocol + Vector events = new Vector(); + String sduType = sdu.type; + if (sduType.equals(OPEN_SUCCESS) || sduType.equals(OPEN_CONFIRMED)) + setState(CONNECTED); + else if (sduType.equals(OPEN_FAILURE) || sduType.equals(CLOSED)) + setState(DISCONNECTED); + return (events); + } + + /** + Sets the messageSize attribute of the TCPService object + * + @param size The new messageSize value + */ + public void setMessageSize(int size) { + messageSize = size; + } + + /** + Set entity service provider + + @param provider service provider + */ + public void setProvider(ProtocolEntity provider) { + this.provider = provider; + } + + /** + Set the Push attribute of a service entity. + + @param push Push value + */ + public void setPush(boolean push) { + this.push = push; + } + + /** + Set the state attribute of a service entity. + + @param state state + */ + public void setState(int state) { + if (DEBUG) + System.err.println("state (" + name + "): " + state); + this.state = state; + } + + /** + Transmit an SDU. + + @param sdu SDU + @param destination destination + */ + public void transmitPDU(PDU sdu, ProtocolEntity destination) { + sdu.setSource(this); + sdu.setDestination(destination); + providerEvents = destination.receivePDU(sdu); + } + +} + diff --git a/lab8_protocols/jasper/source/protocol/TFTP.java b/lab8_protocols/jasper/source/protocol/TFTP.java new file mode 100755 index 0000000..eaee707 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TFTP.java @@ -0,0 +1,29 @@ +// TFTP.java (C) K. J. Turner, K. A. Whyte 04/03/06 + +// Trivial File Transfer Protocol + +package protocol; // protocol package + +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +public class TFTP extends Protocol { // TFTP protocol + + private TFTPSender sender; // protocol sender (client) + private TFTPReceiver receiver; // protocol receiver (server) + + public TFTP() { // construct protocol instance + medium = new TFTPMedium(); // construct comms medium + sender = // construct sender (client) + new TFTPSender(medium, "Client"); + receiver = // construct receiver (server) + new TFTPReceiver(medium, "Server"); + sender.setPeer(receiver); // sender is receiver's peer + receiver.setPeer(sender); // receiver is sender's peer + entities = new Vector(); // initialise protocol entities + entities.addElement(sender); // add sender protocol entity + entities.addElement(medium); // add comms medium entity + entities.addElement(receiver); // add receive protocol entity + } + +} diff --git a/lab8_protocols/jasper/source/protocol/TFTPMedium.java b/lab8_protocols/jasper/source/protocol/TFTPMedium.java new file mode 100755 index 0000000..51fe860 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TFTPMedium.java @@ -0,0 +1,75 @@ +// TFTPMedium.java (C) K. J. Turner 04/03/06 + +package protocol; // protocol package + +import java.util.*; // utility support +import support.*; // protocol entity support + +public class TFTPMedium extends Medium { // protocol medium + + // protocol variables + + private static Vector randoms; // random number list + private static int randomIndex; // random number index + + public TFTPMedium() { // construct medium instance + super(); // construct as generic medium + randoms = new Vector(); // initialise list of randoms + } + + protected PDU getMatchingPDU(String s) { // get matching PDU on channel + PDU pdu; // PDU + int seq = -1; // sequence number + String sdu = ""; // data + int sourceStart = s.indexOf ('[') + 1; // get start of entity name + int sourceEnd = s.indexOf (']'); // get end of entity name + String sourceName = // get PDU source + s.substring(sourceStart, sourceEnd); + int typeStart = s.indexOf (' ') + 1; // get start of PDU type + int typeEnd = s.indexOf ('('); // get end of PDU type + String type = // get PDU type + s.substring(typeStart, typeEnd); + String[] params = getParams(s); // get PDU parameters + if (type.equals("RRQ") || // read request or .. + type.equals("WRQ") || // write request or ... + type.equals("ERROR")) // error? + sdu = params[0]; // get filename/error message + else if (type.equals("ACK")) // acknowledgement? + seq = Integer.parseInt(params[0]); // get sequence number + else { // data + seq = Integer.parseInt(params[0]); // get sequence number + sdu = params[1]; // get data + } + for (Enumeration e = pdus.elements(); // get next PDU on channel ... + e.hasMoreElements(); ) { // as long as more to check + pdu = (PDU) e.nextElement(); // get next PDU on channel + if (pdu != null && // valid PDU and ... + pdu.type.equals(type) && // type matches and ... + pdu.getSource().getName(). // source ... + equals(sourceName) && // matches and ... + seq == pdu.seq && // seq number matches and ... + sdu.equals(pdu.sdu)) // SDU matches + return((pdu)); // return with this PDU + } + return((null)); // return no PDU as no match + } + + public void initialise () { // initialise medium + super.initialise (); // initialise generic medium + randomIndex = 0; // initialise randoms index + } + + protected static float random() { // random number (from list) + Float rand; // random number + + if (randomIndex < randoms.size ()) // number is in list? + rand = (Float) randoms.elementAt(randomIndex++); // get number from list + else { // make new random number + rand = new Float(Math.random()); // get random number + randoms.addElement(rand); // add to list + randomIndex++; // increment list index + } + return((rand.floatValue ())); // return random number + } + +} diff --git a/lab8_protocols/jasper/source/protocol/TFTPMessage.java b/lab8_protocols/jasper/source/protocol/TFTPMessage.java new file mode 100755 index 0000000..0147d7d --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TFTPMessage.java @@ -0,0 +1,38 @@ +// TFTPMessage.java (C) K. J. Turner 01/03/01 + +// Trivial File Transfer Protocol message format + +package protocol; // protocol package + +import support.*; // protocol entity support + +public class TFTPMessage extends PDU { // protocol data unit format + + public TFTPMessage (String type, int seq) { // construct PDU type/seq + super (type, seq); // use generic PDU constructor + } + + public TFTPMessage (String type, String sdu) { // construct PDU type/SDU + super (type, sdu); // use generic PDU constructor + } + + public TFTPMessage ( // construct PDU type/seq/SDU + String type, int seq, String sdu) { + super(type, seq, sdu); // use generic PDU constructor + } + + public String getLabel () { // get PDU "type(contents)" + String label = type; // get PDU type + if (seq >= 0) { // sequence number present? + label += "(" + seq; // append sequence number + if (sdu.equals ("")) // SDU absent? + label += ")"; // finish contents + else // SDU present + label += "," + sdu + ")"; // append SDU, finish contents + } + else if (!sdu.equals ("")) // no seq no, SDU present? + label += "(" + sdu + ")"; // append SDU + return (label); // return PDU label + } + +} diff --git a/lab8_protocols/jasper/source/protocol/TFTPReceiver.java b/lab8_protocols/jasper/source/protocol/TFTPReceiver.java new file mode 100755 index 0000000..b064f64 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TFTPReceiver.java @@ -0,0 +1,232 @@ +// TFTPReceiver.java (C) K. J. Turner, K. A. Whyte 05/06/01 + +// Trivial File Transfer Protocol receiver (server) + +package protocol; // protocol package + +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +/** + This is the class for a Trivial File Transfer Protocol sender. + + @author Kenneth J. Turner, Kenneth A. Whyte + @version 1.0 (5th June 2001, KAW): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): unchecked warning suppressed +*/ +public class TFTPReceiver // protocol receiver (server) + implements ProtocolEntity, Timeouts { // protocol entity, timeout + + // simulator variables + + private ProtocolEntity peer; // peer entity (client) + private Medium medium; // communications medium + private String name; // entity name + + // protocol variables + + private int pduNo; // PDU seq number sent/expected + private PDU pduSent; // PDU sent + private PDU pduReceived; // PDU received + private boolean timerEnabled; // whether timer is enabled + private float errorProb = 0.8f; // file I/O error probability + + // protocol state + + int state; // current protocol state + + final static int idle = 0; // no connection + final static int reading = 2; // reading data + final static int writing = 4; // writing data + final static int waitLast = 5; // wait for last re-send + + // protocol messages + + final static String ack = "ACK"; // acknowledgement message type + final static String data = "DATA"; // data message type + final static String error = "ERROR"; // error message type + final static String rrq = "RRQ"; // read request message type + final static String wrq = "WRQ"; // write request messagetype + + // protocol methods + + public TFTPReceiver(Medium m, String name) { // construct receiver instance + this.name = name; // set protocol entity name + medium = m; // set underlying medium + initialise (); // initialise protocol + } + + public String getName () { // get protocol entity name + return((name)); // return protocol entity name + } + + public Vector getServices() { // get protocol entity services + Vector events = // list of events + new Vector(); + String pduType; // received PDU type + int pduSeq; // received PDU seq number + String pduData; // PDU data + + if (pduReceived != null) { // PDU received? + pduType = pduReceived.type; // get received PDU type + if (pduType.equals(rrq) || // read request or ... + pduType.equals(wrq)) // write request? + pduSeq = 0; // treat as sequence number 0 + else + pduSeq = pduReceived.seq; // get received PDU seq number + pduData = pduReceived.sdu; // get received PDU data + if (((state == idle || // idle or ... + state == reading) && // reading, and ... + pduType.equals(rrq)) || // read request, or ... + ((state == reading || // reading or ... + state == waitLast) && // awaiting re-send, and ... + pduType.equals(ack))) { // acknowledgement? + if (pduSeq == pduNo) { // expected acknowledgement? + timerEnabled = false; // cancel timeout + if (pduSent != null && // valid previous PDU and ... + pduSent.sdu.equals("Dlast")) // last data sent? + initialise (); // re-initialise protocol + else { // more data to send + events.addElement( // send data + data + "(" + (pduNo + 1) + ",D" + (pduNo + 1) + ") - send data"); + events.addElement( // send last data + data + "(" + (pduNo + 1) + ",Dlast) - send last data"); + } + } + else if (pduSeq == pduNo - 1) // repeated acknowledgement? + events.addElement( // send data + data + "(" + pduSent.seq + "," + pduSent.sdu + ") - re-send data"); + } + else if (((state == idle || // idle or ... + state == writing) && // writing, and ... + pduType.equals(wrq)) || // write request, or ... + (state == writing && // writing and ... + pduType.equals(data))) { // data? + if (pduSeq == pduNo || // expected data or ... + pduSeq == pduNo - 1) { // repeated data? + timerEnabled = false; // cancel timeout + events.addElement( // send acknowledgement + ack + "(" + pduSeq + ") - send acknowledgement"); + } + } + } + if (state == waitLast) // waiting for last re-send? + events.addElement( // add close event + "Close - presume all retransmissions over"); + if (state == reading && // reading and ... + pduSent != null && // PDU was sent and ... + !pduSent.sdu.equals("Dlast") && // PDU was not last and ... + TFTPMedium.random() < errorProb) // file read error? + events.addElement( // report reading error + error +"(data for read unavailable) - report error"); + if (timerEnabled) // timer is enabled? + events.addElement( // add timeout event + "Timeout - presume loss of message and resend"); + return(events); // return list of events + } + + public boolean hasTimer(String type) { // protocol uses timer? + return((true)); // report it does + } + + public void initialise () { // initialise protocol + state = idle; // initialise state + pduNo = 0; // initialise seq number + pduReceived = null; // initialise no PDU received + pduSent = null; // initialise no PDU sent + timerEnabled = false; // initialise no timeout + } + + public Vector performService (String s) { // protocol service + Vector events = // initialise events list + new Vector(); + int start, middle, end; // start/middle/end positions + int pduSeq; // PDU sequence number + String pduData; // PDU data + + if (s.startsWith (ack)) { // send acknowledgement? + start = s.indexOf ('(') + 1; // get seq number start + end = s.indexOf (')'); // get seq number end + pduSeq = // get sequence number + Integer.parseInt(s.substring(start, end)); + if (pduReceived != null && // valid PDU received and ... + pduReceived.sdu.equals("Dlast")) // last data? + state = waitLast; // wait for last re-send + else { // still reading + pduNo = pduSeq + 1; // update sequence number + state = writing; // (now) writing + } + transmitPDU( // send acknowledgement + new TFTPMessage(ack, pduSeq), peer); + } + else if (s.startsWith (data)) { // send data? + start = s.indexOf ('(') + 1; // get seq number start + middle = s.indexOf (','); // get comma position + end = s.indexOf (')'); // get seq number end + pduSeq = // get sequence number + Integer.parseInt(s.substring(start, middle)); + pduData = s.substring(middle + 1, end); // get data contents + transmitPDU( // send data + new TFTPMessage(data, pduSeq, pduData), peer); + pduNo = pduSeq; // update sequence number + state = reading; // (now) reading + } + else if (s.startsWith (error)) { // send error? + start = s.indexOf ('(') + 1; // get error message start + end = s.indexOf (')'); // get error message end + pduData = s.substring(start, end); // get error message + transmitPDU( // send error + new TFTPMessage(error, pduData), peer); + state = waitLast; // wait for last re-send + } + else if (s.startsWith ("Close")) { // close? + events.addElement( // add closed comment + new ProtocolEvent(ProtocolEvent.COMMENT, this, "Closed")); + initialise (); // initialise protocol + } + if (s.startsWith ("Timeout")) { // timeout? + transmitPDU(pduSent, peer); // re-send PDU + events.addElement( // add timeout event and PDU + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + else if (pduSent != null) { // PDU to send? + events.addElement( // transmit PDU + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + } + return(events); // return list of events + } + + public Vector receivePDU(PDU pdu) { // handle received PDU + pduReceived = pdu; // store PDU + if (pduReceived.type.equals(error)) // error message? + initialise(); // re-initialise protocol + return((new Vector())); // return no events + } + + public void setPeer(ProtocolEntity peer) { // set protocol peer + this.peer = peer; // set this entity's peer + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { // set timer status + if (state != idle) // not idle? + timerEnabled = enabled; // store timer status + } + + public void transmitPDU( // transmit PDU + PDU pdu, ProtocolEntity dest) { // for given PDU, destination + pdu.setSource (this); // source is this entity + pdu.setDestination(dest); // destination is as given + pduSent = pdu; // copy PDU sent + medium.receivePDU(pdu); // medium receives PDU + pduReceived = null; // note no PDU in response + timerEnabled = false; // note no timeout + } + +} diff --git a/lab8_protocols/jasper/source/protocol/TFTPSender.java b/lab8_protocols/jasper/source/protocol/TFTPSender.java new file mode 100755 index 0000000..e36d0de --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/TFTPSender.java @@ -0,0 +1,263 @@ +// TFTPSender.java (C) K. J. Turner, K. A. Whyte 05/06/01 + +// Trivial File Transfer Protocol sender (client) + +package protocol; // protocol package + +import java.util.Vector; // vector (list) +import support.*; // protocol entity support + +/** + This is the class for a Trivial File Transfer Protocol sender. + + @author Kenneth J. Turner, Kenneth A. Whyte + @version 1.0 (5th June 2001, KAW): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): unchecked warning suppressed +*/ +public class TFTPSender // protocol sender (client) + implements ProtocolEntity, Timeouts { // protocol entity, timeout + + // simulator variables + + private ProtocolEntity peer; // peer entity (server) + private Medium medium; // communications medium + private String name; // entity name + + // protocol variables + + private int pduNo; // PDU seq number sent/expected + private PDU pduReceived; // PDU received + private PDU pduSent; // PDU sent + private boolean timerEnabled; // whether timer is enabled + private float errorProb = 0.8f; // file I/O error probability + private int fileNo; // file number to transfer + + // protocol state + + int state; // current protocol state + + final static int idle = 0; // no connection + final static int readRequest = 1; // file to be read + final static int reading = 2; // reading data + final static int writeRequest = 3; // file to be written + final static int writing = 4; // writing data + final static int waitLast = 5; // wait for last re-send + + // protocol messages + + final static String ack = "ACK"; // acknowledgement message type + final static String data = "DATA"; // data message type + final static String error = "ERROR"; // error message type + final static String rrq = "RRQ"; // read request message type + final static String wrq = "WRQ"; // write request messagetype + + // protocol methods + + public TFTPSender(Medium m, String name) { // construct sender instance + this.name = name; // set protocol entity name + medium = m; // set underlying medium + initialise (); // initialise protocol + } + + public String getName () { // get protocol entity name + return((name)); // return protocol entity name + } + + public Vector getServices() { // get protocol entity services + Vector events = // list of events + new Vector(); + String pduType; // received PDU type + int pduSeq; // received PDU seq number + + if (pduReceived != null) { // PDU received? + pduType = pduReceived.type; // get received PDU type + pduSeq = pduReceived.seq; // get received PDU seq number + if ((state == readRequest || // read request or ... + state == reading || // reading or ... + state == waitLast) && // awaiting re-send, and ... + pduType.equals(data)) { // data message? + if (pduSeq == pduNo) { // expected sequence number? + timerEnabled = false; // cancel timeout + events.addElement( // send acknowledgement + ack + "(" + pduNo + ") - send acknowledgement"); + } + else if (state == readRequest) // wrong sequence, read req? + events.addElement( // report seq number error + error +"(wrong data sequence number) - report error"); + } + else if ((state == writeRequest || // write request or + state == writing) && // writing, and ... + pduType.equals(ack)) { // write and acknowledgement? + if (pduSeq == pduNo) { // expected sequence number? + timerEnabled = false; // cancel timeout + if (pduSent != null && // valid previous PDU and ... + pduSent.sdu.equals("Dlast")) // last data sent? + reinitialise(); // re-initialise protocol + else { // more data to send + events.addElement( // send data + data + "(" + (pduNo + 1) + ",D" + pduNo + ") - send data"); + events.addElement( // send last data + data + "(" + (pduNo + 1) + ",Dlast) - send last data"); + } + } + else if (state == writing && // writing and ... + pduSeq == pduNo - 1) // repeated acknowledgement? + events.addElement( // send data + data + "(" + pduSent.seq + "," + pduSent.sdu + ") - re-send data"); + else if (state == writeRequest) // wrong sequence, write req? + events.addElement( // report seq number error + error +"(wrong acknowledgement sequence number) - report error"); + } + else if (pduType.equals(error)) { // error message? + state = idle; // back to not connected + } + } + if (state == idle) { // not connected? + events.addElement( // read file + rrq + "(file" + fileNo + ") - ask to read file"); + events.addElement( // write file + wrq + "(file" + fileNo +") - ask to write file"); + } + if (state == waitLast) // waiting for last re-send? + events.addElement( // add close event + "Close - presume all retransmissions over"); + if (state == writing && // writing and ... + pduSent != null && // PDU was sent and ... + !pduSent.sdu.equals("Dlast") && // PDU was not last and ... + TFTPMedium.random() < errorProb) // file I/O error occurs? + events.addElement( // report I/O error + error +"(data for write unavailable) - report error"); + if (timerEnabled) // timer is enabled? + events.addElement( // add timeout event + "Timeout - presume loss of message and resend"); + return(events); // return list of events + } + + public boolean hasTimer(String type) { // PDU needs timer? + return((true)); // report it does + } + + public void initialise () { // initialise protocol + fileNo = 0; // initialise file number + reinitialise(); // re-initialise protocol + } + + public Vector performService (String s) { // protocol service + Vector events = // initialise events list + new Vector(); + int start, middle, end; // start/middle/end subscripts + int pduSeq; // PDU sequence number + String pduData; // PDU data + + if (s.startsWith (rrq)) { // send read request? + start = s.indexOf ('(') + 1; // get filename start + end = s.indexOf (')'); // get filename finish + pduData = s.substring(start, end); // get filename + transmitPDU( // send read request + new TFTPMessage(rrq , pduData), peer); + pduNo = 1; // expect seq number 1 + state = readRequest; // now requested write + } + else if (s.startsWith (ack)) { // send acknowledgement? + start = s.indexOf ('(') + 1; // get seq number start + end = s.indexOf (')'); // get seq number end + pduSeq = // get sequence number + Integer.parseInt(s.substring(start, end)); + if (pduReceived != null && // valid PDU received and ... + pduReceived.sdu.equals("Dlast")) // last data? + state = waitLast; // wait for last re-send + else { // still reading + pduNo++; // to next sequence number + state = reading; // (now) reading + } + transmitPDU( // send acknowledgement + new TFTPMessage(ack, pduSeq), peer); + } + else if (s.startsWith (wrq)) { // send write request? + start = s.indexOf ('(') + 1; // get filename start + end = s.indexOf (')'); // get filename finish + pduData = s.substring(start, end); // get filename + transmitPDU( // send write request + new TFTPMessage(wrq , pduData), peer); + state = writeRequest; // now requested write + } + else if (s.startsWith (data)) { // send data? + start = s.indexOf ('(') + 1; // get seq number start + middle = s.indexOf (','); // get comma position + end = s.indexOf (')'); // get seq number end + pduSeq = // get sequence number + Integer.parseInt(s.substring(start, middle)); + pduData = // get data content + s.substring(middle + 1, end); + transmitPDU( // send data + new TFTPMessage(data, pduSeq, pduData), peer); + pduNo++; // to next sequence number + state = writing; // (now) writing + } + else if (s.startsWith (error)) { // send error? + start = s.indexOf ('(') + 1; // get error message start + end = s.indexOf (')'); // get error message end + pduData = s.substring(start, end); // get error message + transmitPDU( // send error + new TFTPMessage(error, pduData), peer); + state = waitLast; // wait for last re-send + } + else if (s.startsWith ("Close")) { // close? + events.addElement( // add closed comment + new ProtocolEvent(ProtocolEvent.COMMENT, this, "Closed")); + reinitialise(); // re-initialise protocol + } + if (s.startsWith ("Timeout")) { // timeout? + transmitPDU(pduSent, peer); // re-send PDU + events.addElement( // add timeout event and PDU + new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); + } + else if (pduSent != null) // PDU to send? + events.addElement( // transmit PDU + new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); + return(events); // return list of events + } + + public Vector receivePDU(PDU pdu) { // handle received PDU + pduReceived = pdu; // store PDU + if (pduReceived.type.equals(error)) // error message? + reinitialise(); // re-initialise protocol + return((new Vector())); // return no events + } + + public void reinitialise() { // re-initialise protocol + state = idle; // initialise state + pduNo = 0; // initialise seq number + pduReceived = null; // initialise no PDU received + pduSent = null; // initialise no PDU sent + timerEnabled = false; // initialise no timeout + fileNo++; // to next file number + } + + public void setPeer(ProtocolEntity peer) { // set protocol peer + this.peer = peer; // set this entity's peer + } + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled) { // set timer status + if (state != idle) // not idle? + timerEnabled = enabled; // store timer status + } + + public void transmitPDU( // transmit PDU + PDU pdu, ProtocolEntity dest) { // for given PDU, destination + pdu.setSource (this); // source is this entity + pdu.setDestination(dest); // destination is as given + pduSent = pdu; // copy PDU sent + medium.receivePDU(pdu); // medium receives PDU + pduReceived = null; // note no PDU in response + timerEnabled = false; // note no timeout + } + +} diff --git a/lab8_protocols/jasper/source/protocol/UDP.java b/lab8_protocols/jasper/source/protocol/UDP.java new file mode 100755 index 0000000..c8f05ab --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/UDP.java @@ -0,0 +1,53 @@ +// UDP.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class UDP extends Protocol { + + private UDPService userA; + private UDPService userB; + private UDPProtocol protA; + private UDPProtocol protB; + + public UDP() { + medium = new UDPMedium(); + userA = new UDPService("User A"); + userB = new UDPService("User B"); + protA = new UDPProtocol(medium, "Protocol A"); + protB = new UDPProtocol(medium, "Protocol B"); + userA.setProvider(protA); + userB.setProvider(protB); + protA.setUser(userA); + protA.setPeer(protB); + protB.setUser(userB); + protB.setPeer(protA); + entities = new Vector(); + entities.addElement(userA); + entities.addElement(protA); + entities.addElement(medium); + entities.addElement(protB); + entities.addElement(userB); + userA.setSourcePort(1111); // initialise A source + userA.setDestPort(2222); // initialise A destination + userB.setSourcePort(3333); // initialise B source + userB.setDestPort(4444); // initialise B destination + } + + public void setParameter(String param, String value) { + try { + int port = Integer.parseInt(value); + if (param.equals("sourcePortA")) + userA.setSourcePort(port); + if (param.equals("destPortA")) + userA.setDestPort(port); + if (param.equals("sourcePortB")) + userB.setSourcePort(port); + if (param.equals("destPortB")) + userB.setDestPort(port); + } + catch (NumberFormatException e) {} + } +} diff --git a/lab8_protocols/jasper/source/protocol/UDPMedium.java b/lab8_protocols/jasper/source/protocol/UDPMedium.java new file mode 100755 index 0000000..28df4d3 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/UDPMedium.java @@ -0,0 +1,49 @@ +// UDPMedium.java (C) I. A. Robin, K. J. Turner 01/03/01 + +package protocol; + +import java.util.*; +import support.*; + +public class UDPMedium extends Medium { + + public UDPMedium() { + super(); + } + + // identify and return PDU currently on channel with + // type and parameters matching those described in given string + // parameters are: (source port, destination port, SDU) + + protected PDU getMatchingPDU(String s) { + UDPMessage ud; + int sp = -1; + int dp = -1; + String sdu = ""; + // extract name of source entity for this datagram: + int sourceStart = s.indexOf('[') + 1; + int sourceEnd = s.indexOf( ']' ); + String sourceName = s.substring(sourceStart, sourceEnd); + // get type and parameters of PDU from action string + // PDU type starts after first space following "Deliver" or "Lose": + int typeStart = s.indexOf(' ') + 1; + int typeEnd = s.indexOf( '(' ); + String type = s.substring(typeStart, typeEnd); + String[] params = getParams(s); + sp = Integer.parseInt(params[0]); // source port + dp = Integer.parseInt(params[1]); // destination port + sdu = params[2]; + // try to match PDU type and seq with a PDU currently on channel: + for (Enumeration e = pdus.elements(); e.hasMoreElements(); ) { + ud = (UDPMessage)e.nextElement(); + if (ud != null && ud.type.equals(type) + && ud.getSource().getName().equals(sourceName) + && sp == ud.sourcePort && dp == ud.destPort + && sdu.equals(ud.getSDU())) { + return ud; + } + } + return null; // no match found + } + +} diff --git a/lab8_protocols/jasper/source/protocol/UDPMessage.java b/lab8_protocols/jasper/source/protocol/UDPMessage.java new file mode 100755 index 0000000..1c5825f --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/UDPMessage.java @@ -0,0 +1,44 @@ +// UDPMessage.java (C) I. A. Robin, K. J. Turner 01/03/01 + +package protocol; + +import support.*; + +public class UDPMessage extends PDU { + + // Class representing User Datagram Protocol Data Unit + + int sourcePort = -1; + int destPort = -1; + + public UDPMessage(String type, int sp, int dp) { + super(type); + sourcePort = sp; + destPort = dp; + } + + public UDPMessage(String type, int sp, int dp, String sdu) { + super(type, sdu); + sourcePort = sp; + destPort = dp; + } + + public boolean matches(PDU pdu) { + if (pdu == null) return false; + UDPMessage ud = (UDPMessage)pdu; + if (type.equals(ud.type) && source == ud.getSource()) { + if (sourcePort == ud.sourcePort + && destPort == ud.destPort + && sdu.equals(ud.getSDU())) + return true; + } + return false; + } + + // returns label for arrow representing UDP PDU in a time sequence diagram + + public String getLabel() { + return type + "(" + sourcePort + "," + destPort+ "," + sdu + ")"; + } + +} diff --git a/lab8_protocols/jasper/source/protocol/UDPProtocol.java b/lab8_protocols/jasper/source/protocol/UDPProtocol.java new file mode 100755 index 0000000..6623fa6 --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/UDPProtocol.java @@ -0,0 +1,81 @@ +// UDPProtocol.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class UDPProtocol implements ProtocolEntity { + + private ProtocolEntity peer; + private UDPService user; + private Vector userEvents; + private Medium medium; + private String name; + + public UDPProtocol(Medium m, String name) { + this.name = name; + medium = m; + initialise(); + } + + public void initialise() { + userEvents = new Vector(); + } + + public String getName() { + return(name); + } + + public void setPeer(ProtocolEntity peer) { + this.peer = peer; + } + + public void setUser(ProtocolEntity user) { + this.user = (UDPService)user; + } + + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + UDPMessage ud; + String pduType = ""; + if (pdu != null) { + pduType = pdu.type; + int sp = ((UDPMessage)pdu).sourcePort; + int dp = ((UDPMessage)pdu).destPort; + if (pduType.equals("DatReq")) { // DatReq from user + ud = new UDPMessage("DT", sp, dp, pdu.getSDU()); + transmitPDU(ud, peer); + events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, ud)); + } + if (pduType.equals("DT")) { // data PDU received + ud = new UDPMessage("DatInd", sp, dp, pdu.getSDU()); + transmitPDU(ud, user); + events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, ud)); + } + } + return(events); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + if (dest == peer) + userEvents = medium.receivePDU(pdu); + else + userEvents = user.receivePDU(pdu); + } + + public Vector getServices() { + Vector list = new Vector(); + return(list); + } + + public Vector performService(String s) { + Vector events = new Vector(); + for (Enumeration e = userEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/protocol/UDPService.java b/lab8_protocols/jasper/source/protocol/UDPService.java new file mode 100755 index 0000000..1569cce --- /dev/null +++ b/lab8_protocols/jasper/source/protocol/UDPService.java @@ -0,0 +1,87 @@ +// UDPService.java (C) I. A. Robin, K. J. Turner 04/03/06 + +package protocol; + +import java.util.*; +import support.*; + +public class UDPService implements ProtocolEntity { + + private static final String SEND = "Send DatReq"; + private static final String SOURCE_PORT = "Source port"; + private static final String DEST_PORT = "Destination port"; + + private ProtocolEntity provider; // protocol for service + private Vector providerEvents; + private PDU pduSent; + private String name; + private int block; // SDU ID number + private int sourcePort = 0; + private int destPort = 0; + + public UDPService(String name) { + this.name = name; + initialise(); + } + + public void initialise() { + block = 0; + providerEvents = new Vector(); + } + + public String getName() { + return(name); + } + + public void setProvider(ProtocolEntity provider) { + this.provider = provider; + } + + public void setSourcePort(int sp) { + sourcePort = sp; + } + + public void setDestPort(int dp) { + destPort = dp; + } + + public Vector receivePDU(PDU pdu) { + return(new Vector()); + } + + public void transmitPDU(PDU pdu, ProtocolEntity dest) { + pdu.setSource(this); + pdu.setDestination(dest); + providerEvents = dest.receivePDU(pdu); + pduSent = pdu; + } + + public Vector getServices() { + Vector list = new Vector(); + String s = SEND + "(D" + block + "): " + + SOURCE_PORT + " " + sourcePort + ", " + + DEST_PORT + " " + destPort; + list.addElement(s); + return(list); + } + + public Vector performService(String s) { + Vector events = new Vector(); + if (s.startsWith(SEND)) { + String sdu = "D" + block; + int spStart = s.indexOf(SOURCE_PORT) + SOURCE_PORT.length() + 1; + int spEnd = s.indexOf(", "); + int dpStart = s.indexOf(DEST_PORT) + DEST_PORT.length() + 1; + int sp = Integer.parseInt(s.substring(spStart, spEnd)); + int dp = Integer.parseInt(s.substring(dpStart)); + PDU pdu = new UDPMessage("DatReq", sp, dp, sdu); + transmitPDU(pdu, provider); + block++; + events.addElement(new ProtocolEvent(ProtocolEvent.SEND, pdu)); + } + for (Enumeration e = providerEvents.elements(); e.hasMoreElements(); ) + events.addElement((ProtocolEvent) e.nextElement()); + return(events); + } + +} diff --git a/lab8_protocols/jasper/source/simulator/CommentSymbol.java b/lab8_protocols/jasper/source/simulator/CommentSymbol.java new file mode 100755 index 0000000..e4c5dcc --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/CommentSymbol.java @@ -0,0 +1,47 @@ +// CommentSymbol.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package simulator; + +import java.awt.*; +import support.ProtocolEvent; + +public class CommentSymbol extends TSDSymbol { + + private boolean lToR; + private String label; + + public CommentSymbol(ProtocolEvent event, String label, + boolean lToR, int col, int top) { + super(event, col, top); + this.lToR = lToR; + this.label = label; + height = 25; + } + + public CommentSymbol(ProtocolEvent event, String label, + boolean lToR, int col, int top, int height) { + super(event, col, top); + this.lToR = lToR; + this.label = label; + this.height = height; + } + + public void draw(Graphics g) { + g.setFont(TimeSequenceDiagram.labelFont); + FontMetrics fm = g.getFontMetrics(TimeSequenceDiagram.labelFont); + if (label.equals("Timeout")) + g.setColor(TimeSequenceDiagram.timeoutColour); + else + g.setColor(TimeSequenceDiagram.commentColour); + int labelWidth = fm.stringWidth(label); + int labelHeight = fm.getHeight(); + int xPos = lToR ? + inset + 10 + col*columnWidth : + inset - 10 + (col + 1)*columnWidth - labelWidth; + //int xPos = lToR ? 2*inset + col*columnWidth : + // (col + 1)*columnWidth - labelWidth; + g.drawString(label, xPos, top + height - labelHeight); + g.setColor(Color.black); + } + +} diff --git a/lab8_protocols/jasper/source/simulator/FailedTransmission.java b/lab8_protocols/jasper/source/simulator/FailedTransmission.java new file mode 100755 index 0000000..a66153f --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/FailedTransmission.java @@ -0,0 +1,58 @@ +// FailedTransmission.java + +package simulator; + +import java.awt.*; +import support.ProtocolEvent; + +/** + This is the class for a failed transmission arrow. + + @author Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.5 (22nd July 2010, KJT): minor tidying +*/ +public class FailedTransmission extends TransmissionSymbol { + + private final static int crossSize = 10; + + /** + Constructor for a traverse transmission arrow. + + @param event protocol event + @param lToR true = left-to-right + @param column column + @param top top of service primitive + @param height height of successful transmission + */ + public FailedTransmission( + ProtocolEvent event, boolean lToR, int column, int top, int height) { + super(event, lToR, column, top, height); + } + + /** + Draw a dashed line with cutoff representing failed transmission through + medium. + + @param g graphics object + */ + public void draw(Graphics g) { + int leftX = inset + col * columnWidth + arrowWidth / 2; + int rightX = leftX + columnWidth - arrowWidth; + int midX = (leftX + rightX) / 2; + int midY = top + height / 2; + g.setColor(Color.blue); + if (lToR) + drawDashedLine(g, leftX, top, midX, midY, dashLength); + else + drawDashedLine(g, rightX, top, midX, midY, dashLength); + // draw a cross to indicate loss of signal + g.drawLine(midX - crossSize / 2, midY - crossSize / 2, + midX + crossSize / 2, midY + crossSize / 2); + g.drawLine(midX + crossSize / 2, midY - crossSize / 2, + midX - crossSize / 2, midY + crossSize / 2); + g.setColor(Color.black); + } + +} + diff --git a/lab8_protocols/jasper/source/simulator/InsetPanel.java b/lab8_protocols/jasper/source/simulator/InsetPanel.java new file mode 100755 index 0000000..8a389a7 --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/InsetPanel.java @@ -0,0 +1,16 @@ +// InsetPanel.java (C) I. A. Robin, K. J. Turner 08/11/00 + +package simulator; + +import java.awt.*; + +public class InsetPanel extends Panel { + + public InsetPanel() { + } + + public Insets getInsets() { + return new Insets(0, 10, 0, 10); + } + +} diff --git a/lab8_protocols/jasper/source/simulator/ProtocolSimulator.java b/lab8_protocols/jasper/source/simulator/ProtocolSimulator.java new file mode 100755 index 0000000..eb1cbda --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/ProtocolSimulator.java @@ -0,0 +1,790 @@ +// ProtocolSimulator.java + +package simulator; // protocol simulator package + +import java.awt.*; +import java.awt.event.*; +import java.applet.*; +import java.io.*; +import java.lang.reflect.*; +import java.util.*; +import javax.swing.*; +import support.Protocol; +import protocol.*; + +/** + This is the main class for protocol simulator. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.1 (22nd December 2000, KJT): revised to support printing +
1.2 (5th March 2001, KJT): minor revisions +
1.3 (6th June 2001, KJT): minor revisions +
1.4 (9th March 2006, KJT): updated for JDK 1.5;, use of Swing + graphics; blank lines ignored in scenario files +
1.5 (20th July 2010, KJT): unchecked warning suppressed; + addition of capability to store/load protocol random numbers + in a scenario file +*/ +public class ProtocolSimulator extends Applet { + + // Constants + + /** Scenario file header */ + private final static String scenPrefix = "Jasper"; + + /** Scenario file suffix */ + private final static String scenSuffix = ".scn"; + + /** Window width */ + private final static int winWidth = 570; + + /** Window height */ + private final static int winHeight = 900; + + /** Auto-run wait (ms) */ + private final static long runWait = 1000; + + /** Auto-run loss probability */ + private final static double lossProb = 0.2; + + // Control variables + + /** Protocol */ + private static Protocol protocol; + + /** Protocol type */ + protected static String protocolType; + + /** Auto-start (if true) */ + private static boolean autoStart = true; + + /** Frame */ + private static JFrame frame; + + /** Time sequence diagram */ + private TimeSequenceDiagram seqDiagram; + + /** Action list */ + private Vector actions; + + /** Saved action list */ + private Vector savedActions; + + /** List being updated (if true) */ + private boolean listUpdate = false; + + /** Stand-alone application (if true) */ + private boolean isStandalone = false; + + /** Simulation running automatically (if true) */ + private boolean running = false; + + // User interface components + + /** Control panel */ + private JPanel panelControl = new JPanel(); + + /** Main panel */ + private JPanel panelMain = new JPanel(); + + /** Button panel */ + private JPanel panelButtons = new JPanel(); + + /** Options panel */ + private JPanel panelOptions = new JPanel(); + + /** Options list */ + private java.awt.List options = new java.awt.List(); + + /** Sequence diagram scrollable pane */ + private ScrollPane sequenceDiagram = new ScrollPane(); + + /** Undo button */ + private JButton undo = new JButton("Undo"); + + /** Redo button */ + private JButton redo = new JButton("Redo"); + + /** Run button */ + private JButton run = new JButton("Run"); + + /** Clear button */ + private JButton clear = new JButton("Clear"); + + /** Print button */ + private JButton print = new JButton("Print"); + + /** Load button */ + private JButton load = new JButton("Load"); + + /** Save button */ + private JButton save = new JButton("Save"); + + /** Overall border layout */ + private BorderLayout borderLayout1 = new BorderLayout(); + + /** Control panel border layout */ + private BorderLayout borderLayout2 = new BorderLayout(); + + /** Main panel border layout */ + private BorderLayout borderLayout3 = new BorderLayout(); + + /** Options panel border layout */ + private BorderLayout borderLayout4 = new BorderLayout(); + + /** Overall grid layout */ + private GridLayout gridLayout1 = new GridLayout(); + + /** + Clear all actions. + + @param actionEvent action event + */ + void actionClear(ActionEvent actionEvent) { + restart(); + undo.setEnabled(false); + redo.setEnabled(false); + clear.setEnabled(false); + } + + // load current actions from scenario file + + /** + Description of the Method + + @param actionEvent action event + */ + void actionLoad(ActionEvent actionEvent) { + String action; // user-selected action + Enumeration enumeration; // enumerated items + String dirName; // enumerated items + String fileName; // chosen directory/file + int linePos = -1; // position in line + String scenProtocol = ""; // scenario protocol + String line; // first file line + BufferedReader file; // output file + FileDialog dialogue = // file dialogue + new FileDialog(frame, "Load Scenario + (name" + scenSuffix + ")", + FileDialog.LOAD); + + dialogue.setVisible(true); // display file dialogue + dirName = dialogue.getDirectory(); // get directory name + fileName = dialogue.getFile(); // get file name + if (dirName != null && fileName != null) // directory/file selected? + try { // try to load file + file = new BufferedReader( // open file + new FileReader(dirName + fileName)); + line = file.readLine(); // read header line + if (line != null) { // header line found? + linePos = line.indexOf(scenPrefix); // get protocol position + if (linePos != -1) { // protocol position found? + int startPos = // get protocol name start + linePos + scenPrefix.length() + 1; + int finishPos = // get following space (if any) + line.indexOf(' ', startPos); + if (finishPos != -1) { // randoms follow protocol? + scenProtocol = // get scenario protocol + line.substring(startPos, finishPos); + String random = // get randoms + line.substring(finishPos + 1); + String[] randomList = // split up random numbers + random.split(","); + Vector randoms = // initialise random numbers + new Vector(); + for (int i = 0; i < randomList.length; i++) { // go through list + String number = randomList[i]; // get random number + try { + float rand = Float.parseFloat(number); // get random number + randoms.add(rand); // append random number + } + catch (NumberFormatException exception) { // format exception? + System.err.println( // report problem + "random number '" + number + "' is not in float format"); + } + } + protocol.setRandomNumbers(randoms); // set random numbers + } + else // no randoms after protocol + scenProtocol = // get scenario protocol + line.substring(startPos); + } + else // header line not found + System.err.println( + fileName + " does not start with correct header"); + } + if (!fileName.endsWith(scenSuffix) || // file does not have suffix? + linePos == -1) + System.err.println(fileName + " is not a scenario file"); + else if (!scenProtocol.equals(protocolType)) // differing protocols? + System.err.println(fileName + " does not match current protocol"); + else { // scenario file matches + restart(); // restart simulation + while ((line = file.readLine()) != null) { // go through file + if (line.matches(".*\\S")) { // non-empty line? + for (enumeration = protocol.getServices().elements(); + enumeration.hasMoreElements(); ) // go through services + enumeration.nextElement(); // do nothing - to next services + actions.addElement(line); // store action + seqDiagram.addEvents( // add action + protocol.performService(line)); + } + // else + // System.err.println("ignored " + line); + } + updateActionList(); // update possible events + sequenceDiagram.validate(); // validate sequence diagram + sequenceDiagram.setScrollPosition( // to end of sequence diagram + 0, seqDiagram.getVerticalPosition()); + clear.setEnabled(!actions.isEmpty()); // enable clear + redo.setEnabled(!savedActions.isEmpty()); // enable redo + undo.setEnabled(!actions.isEmpty()); // enable undo + } + file.close(); // close file + } + catch (IOException exception) { // catch I/O exception + System.err.println("Could not load file " + exception); + } + + } + + /** + Print time sequence diagram. + + @param actionEvent action event + */ + void actionPrint(ActionEvent actionEvent) { + PrintJob printJob = getToolkit().getPrintJob(frame, "Print Diagram", null); + if (printJob != null) { + seqDiagram.printStart(); // start page graphics + do { // loop through pages + Graphics printGraphics = printJob.getGraphics(); + if (printGraphics != null) { + seqDiagram.print(printGraphics); // get page graphics + printGraphics.dispose(); // print page graphics + } + } while (!seqDiagram.printStopped()); // as long as more + printJob.end(); + } + } + + /** + Redo last user selection. + + @param actionEvent action event + */ + void actionRedo(ActionEvent actionEvent) { + if (!savedActions.isEmpty()) { + String action = (String) savedActions.firstElement(); + actions.addElement(action); + savedActions.removeElementAt(0); + seqDiagram.addEvents(protocol.performService(action)); + sequenceDiagram.validate(); + sequenceDiagram.setScrollPosition(0, seqDiagram.getVerticalPosition()); + updateActionList(); + undo.setEnabled(true); + redo.setEnabled(!savedActions.isEmpty()); + clear.setEnabled(true); + } + } + + /** + Run simulation automatically + + @param actionEvent action event + */ + void actionRun(ActionEvent ae) { // start/stop auto run + boolean empty = actions.isEmpty(); + if (running) { // currently running? + running = false; // set not running + run.setText("Run"); // set label to run + clear.setEnabled(!actions.isEmpty()); // enable clear + load.setEnabled(isStandalone); // enable load + print.setEnabled(isStandalone); // enable print + redo.setEnabled(!savedActions.isEmpty()); // enable redo + save.setEnabled(isStandalone); // enable save + undo.setEnabled(!actions.isEmpty()); // enable undo + } + else { // currently stopped + running = true; // set running + run.setText("Stop"); // set label to stop + clear.setEnabled(false); // disable clear + load.setEnabled(false); // disable load + print.setEnabled(false); // disable print + redo.setEnabled(false); // disable redo + save.setEnabled(false); // disable save + load.setEnabled(false); // disable clear + runAction(); // choose random action + } + } + + /** + Save current actions into scenario file. + + @param actionEvent action event + */ + void actionSave(ActionEvent ae) { + String action; // user-selected action + Enumeration enumeration; // enumerated item + String dirName; // enumerated item + String fileName; // chosen directory/file + BufferedWriter file; // output file + FileDialog dialogue = // file dialogue + new FileDialog(frame, "Save Scenario (name" + scenSuffix + ")", + FileDialog.SAVE); + dialogue.setVisible(true); // display file dialogue + dirName = dialogue.getDirectory(); // get directory name + fileName = dialogue.getFile(); // get file name + if (dirName != null && fileName != null) // directory/file selected? + try { // try to save file + if (!fileName.endsWith(scenSuffix)) // file does not have suffix? + fileName += scenSuffix; // append it + file = new BufferedWriter( // open file + new FileWriter(dirName + fileName)); + Vector randoms = // get protocol random numbers + protocol.getRandomNumbers(); + String line = // initialise header line + scenPrefix + " " + protocolType; + if (randoms != null) { // random numbers to save? + line += " "; // append a space + for (int i = 0; i < randoms.size(); i++) { // go through randoms + float random = (Float) randoms.get(i); // get next random + if (i > 0) // not first random? + line += ","; // append comma to line + line += random; // append random + } + } + file.write(line); // output header line + file.newLine(); // append newline + for (enumeration = actions.elements(); // go through actions + enumeration.hasMoreElements(); ) { // as long as more + action = (String) enumeration.nextElement(); // get action + file.write(action); // output action + file.newLine(); // append newline + } + file.close(); // close file + } + catch (IOException exception) { // catch I/O exception + System.err.println("Could not save file " + exception); + } + + } + + /** + Undo last user selection and reconstruct event sequence. + + @param actionEvent action event + */ + void actionUndo(ActionEvent ae) { + boolean empty; + + if (!actions.isEmpty()) { + savedActions.insertElementAt(actions.lastElement(), 0); + actions.removeElementAt(actions.size() - 1); + seqDiagram.clear(); + options.removeAll(); + protocol.init(); + for (Enumeration e = actions.elements(); e.hasMoreElements(); ) { + for (Enumeration dummy = protocol.getServices().elements(); + dummy.hasMoreElements(); ) // get services before redoing + dummy.nextElement(); // do nothing - to next services + String action = (String) e.nextElement(); + seqDiagram.addEvents(protocol.performService(action)); + } + updateActionList(); // update possible events + sequenceDiagram.validate(); + sequenceDiagram.setScrollPosition(0, seqDiagram.getVerticalPosition()); + empty = actions.isEmpty(); + undo.setEnabled(!empty); + redo.setEnabled(true); + clear.setEnabled(!empty); + } + } + + /** + Initialise the simulation. + + @exception any exception + */ + private void appInit() + throws Exception { + protocol = createProtocol(protocolType); // create protocol + seqDiagram = new TimeSequenceDiagram(protocol); + actions = new Vector(); + savedActions = new Vector(); + // component initialisation + setBackground(Color.lightGray); + panelOptions.setFont(TimeSequenceDiagram.labelFont); + panelOptions.setLayout(borderLayout4); + gridLayout1.setRows(4); + gridLayout1.setHgap(10); + gridLayout1.setColumns(2); + gridLayout1.setVgap(5); + setLayout(borderLayout1); + panelControl.setLayout(borderLayout2); + panelMain.setLayout(borderLayout3); + add(panelMain, BorderLayout.CENTER); + panelMain.add(sequenceDiagram, BorderLayout.CENTER); + panelMain.add(panelControl, BorderLayout.SOUTH); + panelButtons.setLayout(gridLayout1); + panelButtons.add(undo, null); + panelButtons.add(redo, null); + panelButtons.add(run, null); + panelButtons.add(clear, null); + panelButtons.add(load, null); + load.setEnabled(isStandalone); + panelButtons.add(save, null); + save.setEnabled(isStandalone); + panelButtons.add(print, null); + print.setEnabled(isStandalone); + add(panelControl, BorderLayout.SOUTH); + panelControl.add(panelButtons, BorderLayout.WEST); + panelControl.add(panelOptions, BorderLayout.CENTER); + panelOptions.add(options, BorderLayout.CENTER); + sequenceDiagram.add(seqDiagram, null); + clear.addActionListener( + new ActionListener() { // add clear button + public void actionPerformed(ActionEvent e) { + actionClear(e); + } + }); + load.addActionListener( + new ActionListener() { // action load button + public void actionPerformed(ActionEvent e) { + actionLoad(e); + } + }); + print.addActionListener( + new ActionListener() { // add print button + public void actionPerformed(ActionEvent e) { + actionPrint(e); + } + }); + redo.addActionListener( + new ActionListener() { // add redo button + public void actionPerformed(ActionEvent e) { + actionRedo(e); + } + }); + run.addActionListener( + new ActionListener() { // add run button + public void actionPerformed(ActionEvent e) { + actionRun(e); + } + }); + save.addActionListener( + new ActionListener() { // add save button + public void actionPerformed(ActionEvent e) { + actionSave(e); + } + }); + undo.addActionListener( + new ActionListener() { // add undo button + public void actionPerformed(ActionEvent e) { + actionUndo(e); + } + }); + options.addItemListener( + new ItemListener() { // add options list + public void itemStateChanged(ItemEvent e) { + itemOptions(e); + if (isStandalone) // running as application? + options.select(0); // set item for any double-click + } + }); + } + + /** + Create protocol for type. + + @param type protocol type + @return protocol entity + @exception exception if invalid type + */ + public static Protocol createProtocol(String type) throws Exception { + Protocol protocol = null; // protocol instance + Class subClass; // protocol instance + Class superClass; // protocol subclass/superclass + Constructor subCons; // subclass constructor + String subType = ""; // protocol subtype + int subPos; // subtype position + Class consPars[] = new Class[1]; // constructor parameters + Object instPars[] = new Object[1]; // instance parameters + + try { // try to create protocol + subPos = type.indexOf("/"); // look for protocol subtype + if (subPos != -1) { // subtype present? + subType = type.substring(subPos + 1); // set subtype + type = type.substring(0, subPos); // set type + } + subClass = Class.forName("protocol." + type); // get class for type name + superClass = subClass.getSuperclass(); // get its superclass + if (superClass != null && // superclass is Protocol? + superClass.getName().equals("support.Protocol")) { + if (subType.equals("")) // subtype absent? + protocol = // create subclass instance + (Protocol) subClass.newInstance(); + else { // subtype present + consPars[0] = subType.getClass(); // set up constructor params + try { // try to construct + subCons = // get subclass constructor + subClass.getConstructor(consPars); + instPars[0] = subType; // set up instance params + protocol = // create subclass instance + (Protocol) subCons.newInstance(instPars); + } + catch (Exception exception) { // cannot construct + exception.printStackTrace(); + throw new Exception( + "protocol '" + type + "' does not have subtype '" + + subType + "'"); + } + } + } + else // superclass not Protocol + throw new Exception("invalid protocol type '" + type + "'"); + } + catch (Throwable thrown) { // cannot create protocol + throw new Exception( // report exception + "Cannot create protocol: " + thrown.getMessage()); + } + return (protocol); // return protocol instance + } + + /** + Return a parameter value. + + @param key Parameter + @param def Parameter + @return The parameter value + */ + public String getParameter(String key, String def) { + return isStandalone ? System.getProperty(key, def) : + (getParameter(key) != null ? getParameter(key) : def); + } + + /** + Return parameter information. + + @return The parameterInfo value + */ + public String[][] getParameterInfo() { + String pinfo[][] = { + {"Protocol", "String", "Protocol type"}, + }; + return pinfo; + } + + /** + Initialise the simulation. + */ + public void init() { + if (!isStandalone) + try { + protocolType = getParameter("protocol", "ABP"); + autoStart = getParameter("start", "true").equals("true"); + } + catch (Exception e) { + e.printStackTrace(); + } + + try { + appInit(); + } + catch (Exception e) { + System.err.println(e.getMessage()); + // e.printStackTrace(); + System.exit(1); + } + } + + /** + Show effect of user choice in diagram. + + @param ie Parameter + */ + void itemOptions(ItemEvent ie) { + String action; // user-selected action + + if (!listUpdate) { // list not being updated? + action = options.getSelectedItem(); + actions.addElement(action); + savedActions.removeAllElements(); + seqDiagram.addEvents(protocol.performService(action)); + sequenceDiagram.validate(); + sequenceDiagram.setScrollPosition(0, seqDiagram.getVerticalPosition()); + updateActionList(); + if (running) // currently running? + runAction(); // choose random action + else { // not currently running + undo.setEnabled(true); + redo.setEnabled(false); + clear.setEnabled(true); + } + } + } + + /** + The main program for the protocol simulator class. + + @param args command-line arguments + */ + public static void main(String[] args) { + ProtocolSimulator applet = new ProtocolSimulator(); + applet.isStandalone = true; + protocolType = args.length > 0 ? args[0] : "ABP"; + frame = new JFrame(); + frame.setTitle(protocolType + " Protocol Simulator"); + frame.add(applet, BorderLayout.CENTER); + frame.addWindowListener(new FrameWindowAdapter(applet)); + autoStart = true; + applet.init(); + // parse and set parameters from command-line arguments + int i = 1; + while (i < args.length) { + int eq = args[i].indexOf('='); + if (eq > 0) { + String param = args[i].substring(0, eq); + String value = args[i].substring(eq + 1); + protocol.setParameter(param, value); + i++; + } + } + applet.start(); + frame.pack(); + frame.setSize(winWidth, winHeight); + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation( + (d.width - frame.getSize().width) / 2, + (d.height - frame.getSize().height) / 2); + frame.setVisible(true); + } + + /** + Restart simulation from scratch. + */ + public void restart() { + seqDiagram.clear(); + sequenceDiagram.setScrollPosition(0, seqDiagram.getVerticalPosition()); + sequenceDiagram.validate(); + actions.removeAllElements(); + savedActions.removeAllElements(); + protocol.init(); + updateActionList(); + undo.setEnabled(false); + redo.setEnabled(false); + clear.setEnabled(false); + } + + /** + Perform a random action during running + */ + void runAction() { + Thread runChoice = + new Thread() { // make thread for auto-run + public void run() { // run thread + int numChoices = // get number of choices + options.getItemCount(); + int selChoice; // selected choice + if (numChoices >= 1) { // at least one choice? + try { // try to wait + Thread.sleep(runWait); // wait before selecting + } + catch (InterruptedException exception) { // catch interrupt + } + do { + selChoice = // choose random action + (int) (Math.random() * numChoices); + } + while (numChoices > 1 && // more than one choice? + options.getItem(selChoice). // choice is message loss? + indexOf(": Lose") != -1 && + Math.random() > lossProb); // exceeds loss probability? + options.select(selChoice); // select random action + options.dispatchEvent( // simulate list click + new ItemEvent(options, ItemEvent.ITEM_STATE_CHANGED, + options, ItemEvent.SELECTED)); + } + } + }; + runChoice.start(); // start auto-run thread + } + + /** + Generic method to pass parameter strings supplied from JavaScript on the web + page into the protocol class. + + @param parameter parameter name + @param value parameter value + */ + public void setParameter(String parameter, String value) { + protocol.setParameter(parameter, value); + } + + /** + Start simulation. + */ + public void start() { + if (autoStart) + updateActionList(); + undo.setEnabled(false); + redo.setEnabled(false); + clear.setEnabled(false); + } + + /** + Update list of possible events. + */ + public void updateActionList() { + listUpdate = true; + options.removeAll(); + for (Enumeration e = protocol.getServices().elements(); + e.hasMoreElements(); ) + options.add((String) e.nextElement()); + listUpdate = false; + } + + /** + Handle window closing event. + + @param windowEvent window event + */ + void windowClosing(WindowEvent windowEvent) { + System.exit(0); + } + +} + +/** + This is a supporting class for adapting windows. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +class FrameWindowAdapter extends java.awt.event.WindowAdapter { + + /** Adapted protocol simulator window */ + ProtocolSimulator adaptee; + + /** + Constructor for a frame window adaptor. + + @param adaptee Parameter + */ + FrameWindowAdapter(ProtocolSimulator adaptee) { + this.adaptee = adaptee; + } + + /** + Handle window closing event. + + @param windowEvent window event + */ + public void windowClosing(WindowEvent windowEvent) { + adaptee.windowClosing(windowEvent); + } + +} + diff --git a/lab8_protocols/jasper/source/simulator/ServicePrimitive.java b/lab8_protocols/jasper/source/simulator/ServicePrimitive.java new file mode 100755 index 0000000..951086d --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/ServicePrimitive.java @@ -0,0 +1,98 @@ +// ServicePrimitive.java (C) I. A. Robin, K. J. Turner 08/03/06 + +package simulator; + +import java.awt.*; +import support.ProtocolEvent; + +public class ServicePrimitive extends TSDSymbol { + + public static final int HEAD = 0; + public static final int TAIL = 1; + + private static final int labelPadding = 5; + private static int headLength = 8; + private static int headWidth = 5; + private int width; + private boolean lToR; + private String label; + private int labelPos; + + public ServicePrimitive( + ProtocolEvent event, boolean lToR, int col, int top, int height) { + super(event, col, top); + this.lToR = lToR; + width = arrowWidth; + this.height = height; + } + + public void setLabel(String lbl, int pos) { + label = lbl; + labelPos = pos; + } + + // Draw filled triangle at head of arrow + // l = length, w = width of arrowhead + + private void drawHead( + Graphics g, int xHead, int yHead, int xTail, int yTail, int l, int w) { + int dx = xHead - xTail; + int dy = yHead - yTail; + int[] xh = new int[3]; + int[] yh = new int[3]; + xh[0] = xHead; + yh[0] = yHead; + float length = (float)(Math.sqrt(dx*dx + dy*dy)); + xh[1] = xh[0] - Math.round((l*dx + w*dy)/length); + yh[1] = yh[0] + Math.round((w*dx - l*dy)/length); + xh[2] = xh[0] - Math.round((l*dx - w*dy)/length); + yh[2] = yh[0] - Math.round((w*dx + l*dy)/length); + g.fillPolygon(xh, yh, 3); + } + + private void drawLabel( + Graphics g, int xHead, int yHead, int xTail, int yTail) { + g.setFont(TimeSequenceDiagram.labelFont); + FontMetrics fm = g.getFontMetrics(TimeSequenceDiagram.labelFont); + int labelWidth = fm.stringWidth(label); + int labelX = xTail - labelWidth - labelPadding; + int labelY = (labelPos == HEAD)? yHead : yTail; + if (lToR && labelPos == HEAD) + labelX = xHead + labelPadding; + if (!lToR && labelPos == TAIL) + labelX = xTail + labelPadding; + if (!lToR && labelPos == HEAD) + labelX = xHead - labelWidth - labelPadding; + g.drawString(label, labelX, labelY - 3); + } + + // Draw arrow across column boundary from sourceCol + // in specified direction (lToR = true/false) + // with text label at "labelPos" (HEAD / TAIL) + + public void draw(Graphics g) { + int xMid = inset + col*columnWidth; // midpoint of shaft + if (!lToR) xMid += columnWidth; + // Locations of arrow head and tail + int xHead = xMid + width/2; + int xTail = xMid - width/2; + if (!lToR) { // adjust for rightwards arrow + xHead -= width; + xTail += width; + } + int yTail = top; + int yHead = yTail + height; + drawLabel(g, xHead, yHead, xTail, yTail); // draw label + g.drawLine(xTail, yTail, xHead, yHead); // draw shaft of arrow + if (lToR) { // prepare head of arrow + g.drawLine(xTail, yTail - 1, xHead - 4, yHead - 1); + xHead += 2; + } + else { + g.drawLine(xTail, yTail - 1, xHead + 4, yHead - 1); + xHead -= 2; + } + drawHead(g, xHead, yHead, xTail, yTail, headLength, headWidth); + } + +} diff --git a/lab8_protocols/jasper/source/simulator/SpaceFiller.java b/lab8_protocols/jasper/source/simulator/SpaceFiller.java new file mode 100755 index 0000000..148a342 --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/SpaceFiller.java @@ -0,0 +1,21 @@ +// SpaceFiller.java (C) I. A. Robin, K. J. Turner 08/11/00 + +package simulator; + +import java.awt.*; + +public class SpaceFiller extends TSDSymbol { + + // creates a gap between symbols in Time Sequence Diagram + // to avoid overlapping + + public SpaceFiller(int col, int top) { + super(null, col, top); + } + + // do nothing! + + public void draw(Graphics g) { + } + +} diff --git a/lab8_protocols/jasper/source/simulator/SuccessfulTransmission.java b/lab8_protocols/jasper/source/simulator/SuccessfulTransmission.java new file mode 100755 index 0000000..a903c96 --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/SuccessfulTransmission.java @@ -0,0 +1,50 @@ +// SuccessfulTransmission.java + +package simulator; + +import java.awt.*; // import Java AWT classes + +import support.*; // import Jasper protocol event + +/** + This is the class for a successful transmission arrow. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.5 (22nd July 2010, KJT): minor tidying +*/ +public class SuccessfulTransmission extends TransmissionSymbol { + + /** + Constructor for a successful transmission arrow. + + @param event protocol event + @param lToR true = left-to-right + @param col column + @param top top of service primitive + @param height height of successful transmission + */ + public SuccessfulTransmission( + ProtocolEvent event, boolean lToR, int col, int top, int height) { + super(event, lToR, col, top, height); + } + + /** + Paint the successful transmission as a dashed line representing transmission + through the medium. + + @param g graphics object + */ + public void draw(Graphics g) { + int leftX = inset + col * columnWidth + arrowWidth / 2; + int rightX = leftX + columnWidth - arrowWidth; + g.setColor(Color.blue); + if (lToR) + drawDashedLine(g, leftX, top, rightX, top + height, dashLength); + else + drawDashedLine(g, rightX, top, leftX, top + height, dashLength); + g.setColor(Color.black); + } + +} + diff --git a/lab8_protocols/jasper/source/simulator/TSDSymbol.java b/lab8_protocols/jasper/source/simulator/TSDSymbol.java new file mode 100755 index 0000000..c29a47d --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/TSDSymbol.java @@ -0,0 +1,108 @@ +// TSDSymbol.java + +package simulator; + +import java.awt.*; +import support.ProtocolEvent; + +/** + This is the class for a general time sequence diagram symbol. + + @author Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.5 (22nd July 2010, KJT): minor tidying +*/ +public abstract class TSDSymbol { + + /** Y-coordinate for symbol top */ + protected int top; + + /** Destination column number */ + protected int col; + + /** Symbol height */ + protected int height = 15; + + /** Inset around diagram */ + protected static int inset; + + /** Diagram column width */ + protected static int columnWidth; + + /** Primitive arrow width */ + protected static int arrowWidth; + + /** Protocol event */ + protected ProtocolEvent event; + + /** + Constructor for the TSDSymbol object + * + @param event protocol event + @param col symbol column + @param top symbol top + */ + public TSDSymbol(ProtocolEvent event, int col, int top) { + this.event = event; + this.col = col; + this.top = top; + } + + /** + Sets the parameters attribute of the TSDSymbol class + + @param i The new parameters value + @param c The new parameters value + @param w The new parameters value + */ + public static void setParameters(int i, int c, int w) { + inset = i; + columnWidth = c; + arrowWidth = w; + } + + /** + Get the event attribute of a time sequence diagram symbol. + + @return The event value + */ + public ProtocolEvent getEvent() { + return event; + } + + /** + Get the top attribute of a time sequence diagram symbol. + + @return The top value + */ + public int getTop() { + return top; + } + + /** + Get the column attribute of a time sequence diagram symbol. + + @return The col value + */ + public int getCol() { + return col; + } + + /** + Get the height attribute of a time sequence diagram symbol. + + @return The height value + */ + public int getHeight() { + return height; + } + + /** + Empty draw method to be overridden. + + @param g graphics object + */ + public abstract void draw(Graphics g); + +} + diff --git a/lab8_protocols/jasper/source/simulator/TimeSequenceDiagram.java b/lab8_protocols/jasper/source/simulator/TimeSequenceDiagram.java new file mode 100755 index 0000000..c9e3d99 --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/TimeSequenceDiagram.java @@ -0,0 +1,594 @@ +// TimeSequenceDiagram.java + +package simulator; // simulator package + +import java.awt.*; // import Java AWT classes + +import java.util.*; // import Java utility classes + +import support.*; // import Jasper support classes + +/** + This is the class for a time sequence diagram. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.4 (9th March 2006, KJT): updated for JDK 1.5, use of Swing + graphics, minor tidying +
1.5 (27th July 2010, KJT): minor tidying; vertical space before + DELIVER; vertical space before COMMENT not just for first + event; addition of TRAVERSE for events that span columns +*/ +public class TimeSequenceDiagram extends Canvas { + + /** Vertical extent of primitive */ + private final static int primitiveHeight = 10; + + /** Width of primitive arrow */ + private final static int arrowWidth = 30; + + /** Top/left insert of diagram */ + private final static int inset = 15; + + /** Page left-hand margin */ + private final static int margin = 15; // + + /** Maximum printing height */ + private final static int maxHeight = 800; + + /** Max symbols to print */ + private final static int maxSymbols = 64; + + /** Diagram/page header font */ + public final static Font headerFont = new Font("Sans serif", Font.BOLD, 14); + + /** Page footer font */ + public final static Font footerFont = new Font("Sans serif", Font.BOLD, 14); + + /** Diagram label font */ + public final static Font labelFont = new Font("Sans serif", Font.PLAIN, 13); + + /** Mid green comments */ + public final static Color commentColour = new Color(0, 128, 0); + + /** Red last symbol */ + public final static Color lastColour = Color.red; + + /** Tan timeouts */ + public final static Color timeoutColour = new Color(128, 64, 48); + + /** Tan timeouts */ + private String[] columnHeaders; + + /** Header text height */ + private int headerTextHeight; + + /** Column width in pixels */ + private int columnWidth; + + /** Number of columns */ + private int columns; + + /** Y-coordinate for symbol area top */ + private int topOfBlock; + + /** Default symbol height */ + private int defaultSymbolHeight; + + /** Vertical scroll position */ + private int verticalPosition; + + /** List of symbols */ + private Vector symbols; + + /** List of protocol entities */ + private Vector entities; + + /** Printing starting */ + private boolean starting; + + /** Printing finished */ + private boolean stopped; + + /** Y-coordinate for initial symbol */ + private int initialHeight; + + /** Medium column number */ + private int mediumColumn; + + /** Last column number */ + private int lastColumn; + + /** Y-coordinate for start of SEND arrow */ + private int start = 0; + + /** Current printing top */ + private int currentHeight; + + /** Current page number */ + private int page; + + /** + Constructor for the TimeSequenceDiagram object + + @param protocol Parameter + */ + public TimeSequenceDiagram(Protocol protocol) { + setBackground(Color.white); + entities = protocol.getEntities(); + mediumColumn = protocol.getMediumColumn(); + columns = entities.size(); + columnHeaders = new String[columns]; + for (int i = 0; i < columns; i++) + columnHeaders[i] = ((ProtocolEntity) entities.elementAt(i)).getName(); + symbols = new Vector(); + verticalPosition = 0; + lastColumn = -1; + starting = true; + } + + /** + Adds a feature to the CommentSymbol attribute of the TimeSequenceDiagram + object. + + @param comment comment text + @param lToR true = left-to-right + @param sourceCol source column + */ + private void addCommentSymbol(String comment, boolean lToR, int sourceCol) { + CommentSymbol st = + new CommentSymbol(null, comment, lToR, sourceCol, topOfBlock); + addSymbol(st); + } + + // determine types of protocol events + // and add appropriate symbols to diagram + + /** + Add events to time sequence diagram. + + @param events event list + */ + public void addEvents(Vector events) { + int eventNo = 0; + int source = 0; + int destination = 0; + for (Enumeration e = events.elements(); e.hasMoreElements(); ) { + ProtocolEvent event = (ProtocolEvent) e.nextElement(); + PDU pdu = null; + if (event != null) { + int eventType = event.getType(); + pdu = event.getPDU(); + if (pdu != null) { + source = entities.indexOf(pdu.getSource()); + destination = entities.indexOf(pdu.getDestination()); + boolean lToR = destination > source; + int col = lToR ? source + 1 : source - 1; + String label = pdu.getLabel(); + int transmissionHeight = defaultSymbolHeight; + switch (eventType) { + case ProtocolEvent.SEND: + addSpace(source); + addServicePrimitive( + event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL); + break; + case ProtocolEvent.TRANSMIT: + addSpace(source); + addServicePrimitive( + event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL); + break; + case ProtocolEvent.RECEIVE: + start = getTransmissionStart(pdu); + if (start != topOfBlock) + transmissionHeight = Math.max( + defaultSymbolHeight, topOfBlock - start + primitiveHeight); + if (destination == lastColumn) + transmissionHeight += 7; + addSuccessfulTransmission( + event, lToR, mediumColumn, start, transmissionHeight); + addServicePrimitive( + event, lToR, destination, start + transmissionHeight, + label, ServicePrimitive.HEAD); + lastColumn = destination; + break; + case ProtocolEvent.DELIVER: + addSpace(source); + addServicePrimitive( + event, lToR, destination, topOfBlock, label, + ServicePrimitive.HEAD); + break; + case ProtocolEvent.FRAGMENT: + if (eventNo == 0) + start = getTransmissionStart(pdu); + if (start != topOfBlock) + transmissionHeight = Math.max( + defaultSymbolHeight, topOfBlock - start + primitiveHeight); + if (destination == lastColumn) + transmissionHeight += 7; + addSuccessfulTransmission( + event, lToR, mediumColumn, start, transmissionHeight); + addServicePrimitive( + event, lToR, destination, start + transmissionHeight, + label, ServicePrimitive.HEAD); + lastColumn = destination; + break; + case ProtocolEvent.LOSE: + if (eventNo == 0) + start = getTransmissionStart(pdu); + if (start != topOfBlock) + transmissionHeight = Math.max( + defaultSymbolHeight, topOfBlock - start + primitiveHeight); + if (destination == lastColumn) + transmissionHeight += 7; + addFailedTransmission( + event, lToR, mediumColumn, start, transmissionHeight); + lastColumn = destination; + break; + case ProtocolEvent.TIMEOUT: + if (eventNo == 0) + addSpace(source); + addSenderTimeout(event, lToR, source); + addServicePrimitive( + event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL); + break; + case ProtocolEvent.TRAVERSE: + source++; // incr. source column + int columns = destination - source; // set number of columns + start = getTransmissionStart(pdu); // get y for start + if (start != topOfBlock) // not top of block? + transmissionHeight = Math.max( // set trans. height + defaultSymbolHeight, topOfBlock - start + primitiveHeight); + if (columns > 1) + transmissionHeight = + columns*(transmissionHeight + primitiveHeight); + if (destination == lastColumn) // last column? + transmissionHeight += 7; // allow extra space + addTraverseTransmission( // add traverse trans. + event, lToR, source, columns, start, transmissionHeight); + addServicePrimitive( // add service prim. + event, lToR, destination, start + transmissionHeight, + label, ServicePrimitive.HEAD); + lastColumn = destination; // set last column + break; + } + } + if (eventType == ProtocolEvent.COMMENT) { + // if (eventNo == 0) + addSpace(source); + int col = entities.indexOf(event.getTarget()); + addCommentSymbol(event.getComment(), col < mediumColumn, col); + } + } + eventNo++; + } + } + + /** + Adds a failed transmission to a time sequence diagram. + + @param event protocol event + @param lToR true = left-to-right + @param mediumColumn medium column + @param top top of service primitive + @param height height of successful transmission + */ + private void addFailedTransmission( + ProtocolEvent event, boolean lToR, int mediumColumn, int top, int height) { + FailedTransmission t = + new FailedTransmission(event, lToR, mediumColumn, top, height); + addSymbol(t); + } + + /** + Adds a feature to the SenderTimeout attribute of the TimeSequenceDiagram + object. + + @param event protocol event + @param lToR true = left-to-right + @param sourceCol source column + */ + private void addSenderTimeout( + ProtocolEvent event, boolean lToR, int sourceCol) { + CommentSymbol st = + new CommentSymbol(event, "timeout", lToR, sourceCol, topOfBlock); + addSymbol(st); + } + + /** + Adds a feature to the ServicePrimitive attribute of the TimeSequenceDiagram + object. + + @param event protocol event + @param lToR true = left-to-right + @param destination destination column + @param top top of service primitive + @param label label for service primitive + @param pos position for label + */ + private void addServicePrimitive( + ProtocolEvent event, boolean lToR, int destination, int top, + String label, int pos) { + ServicePrimitive sp = + new ServicePrimitive(event, lToR, destination, top, primitiveHeight); + sp.setLabel(label, pos); + addSymbol(sp); + } + + /** + Adds a feature to the Space attribute of the TimeSequenceDiagram object + + @param col column + */ + private void addSpace(int col) { + addSymbol(new SpaceFiller(col, topOfBlock)); + } + + /** + Adds a successful transmission to a time sequence diagram. + + @param event protocol event + @param lToR true = left-to-right + @param mediumColumn medium column + @param top top of service primitive + @param height height of successful transmission + */ + private void addSuccessfulTransmission( + ProtocolEvent event, boolean lToR, int mediumColumn, int top, int height) { + SuccessfulTransmission t = + new SuccessfulTransmission(event, lToR, mediumColumn, top, height); + addSymbol(t); + } + + /** + Adds a feature to the Symbol attribute of the TimeSequenceDiagram object + + @param symbol The feature to be added to the Symbol attribute + */ + private void addSymbol(TSDSymbol symbol) { + int height = getSize().height; + int symbolHeight = symbol.getHeight(); + int top = symbol.getTop(); + topOfBlock = top + symbolHeight; + symbols.addElement(symbol); + if (topOfBlock + symbolHeight + 2 * inset >= height) { + int width = getSize().width; + setSize(width, topOfBlock + symbolHeight + 2 * inset); + } + verticalPosition = getSize().height - initialHeight; + repaint(); + } + + /** + Adds a traverse transmission to a time sequence diagram. + + @param event protocol event + @param lToR true = left-to-right + @param source source column + @param columns columns + @param top top of service primitive + @param height height of traverse transmission + */ + private void addTraverseTransmission( + ProtocolEvent event, boolean lToR, int source, int columns, int top, + int height) { + TraverseTransmission t = + new TraverseTransmission(event, lToR, source, columns, top, height); + addSymbol(t); + } + + /** + Remove everything from the time sequence diagram. + */ + public void clear() { + symbols.removeAllElements(); + verticalPosition = 0; + setSize(getSize().width, initialHeight); + topOfBlock = 3 * inset + headerTextHeight; + repaint(); + } + + /** + Draw the columns of the time sequence diagram. + + @param g graphics object + @param margin column margin + @param width column width + @param height column height + */ + private void drawColumns(Graphics g, int margin, int width, int height) { + int headerWidth; + int columnCentre; + g.setFont(headerFont); // column headings + FontMetrics fm = getFontMetrics(headerFont); + headerTextHeight = fm.getHeight(); + for (int c = 0; c < columns; c++) { + columnCentre = margin + inset + (2 * c + 1) * columnWidth / 2; + headerWidth = fm.stringWidth(columnHeaders[c]); + g.drawString( + columnHeaders[c], columnCentre - headerWidth / 2, + inset + fm.getAscent()); + } + g.drawLine( + margin + inset, inset + headerTextHeight, + margin + width - inset, inset + headerTextHeight); + for (int c = 1; c < columns; c++) // column separators + g.drawLine( + margin + inset + c * columnWidth, inset, + margin + inset + c * columnWidth, height - inset); + + } + + /** + Gets the transmissionStart attribute of the TimeSequenceDiagram object. + + @param pdu PDU associated with the TRANSMIT event + @return Y-coordinate of the end of the SEND event arrow from + which the corresponding TRANSMIT event symbol will start + */ + private int getTransmissionStart(PDU pdu) { + int y = topOfBlock; + for (Enumeration e = symbols.elements(); e.hasMoreElements(); ) { + TSDSymbol symbol = (TSDSymbol) e.nextElement(); + ProtocolEvent event = symbol.getEvent(); + if (event != null) { + PDU pduSent = event.getPDU(); + if (pdu != null && pduSent.matches(pdu)) + y = symbol.getTop() + symbol.getHeight(); + } + } + return(y); + } + + /** + Get the verticalPosition attribute of the TimeSequenceDiagram object. + + @return vertical position + */ + public int getVerticalPosition() { + return(verticalPosition); + } + + /** + Paint the time sequene diagram. + + @param g graphics object + */ + public void paint(Graphics g) { + TSDSymbol symbol; + int width = getSize().width; + int height = getSize().height; + columnWidth = (width - 2 * inset) / columns; + TSDSymbol.setParameters(inset, columnWidth, arrowWidth); + defaultSymbolHeight = + (columnWidth - arrowWidth) * primitiveHeight / arrowWidth; + drawColumns(g, 0, width, height); + if (starting) { + initialHeight = height; + topOfBlock = 3 * inset + headerTextHeight; + starting = false; + } + for (Enumeration e = symbols.elements(); e.hasMoreElements(); ) { + symbol = (TSDSymbol) e.nextElement(); + if (!e.hasMoreElements()) // colour last symbol + g.setColor(lastColour); + symbol.draw(g); + } + } + + /** + Print the time sequence diagram. + + @param g graphcs object + */ + public void print(Graphics g) { + TSDSymbol symbol = null; // current symbol + TSDSymbol symbolBuff[] = // symbols to print + new TSDSymbol[maxSymbols]; + int nextSymbol = 0; // next symbol position + int sym; // symbol count + int width = getSize().width; // diagram width + int height = getSize().height; // diagram height + int offset; // offset from page top + int nextHeight; // next printing top + int nullHeight = 0; // height at last null + + if (starting) { // first page? + starting = false; // note not first page + stopped = false; // printing not finished + offset = 0; // set first page offset + currentHeight = 0; // set symbol height + page = 0; // set page number + System.out.print("Printing " + // start page listing + ProtocolSimulator.protocolType + " page"); + } + else // not first page + offset = // set later page offset + 3 * inset + headerTextHeight - currentHeight; + nextHeight = currentHeight + maxHeight; // next printing top + columnWidth = (width - 2 * inset) / columns; // set column width + TSDSymbol.setParameters( // set symbol details + margin + inset, columnWidth, arrowWidth); + defaultSymbolHeight = // set transm. height + (columnWidth - arrowWidth) * primitiveHeight / arrowWidth; + drawColumns(g, margin, width, height); // draw column headers + System.err.print(" " + ++page); // print page number + for (Enumeration e = symbols.elements(); // go through symbols + e.hasMoreElements(); ) { + symbol = (TSDSymbol) e.nextElement(); // get next symbol + if (symbol.getClass() == SpaceFiller.class) { // symbol gap? + if (symbol.top + symbol.height >= nextHeight) { // past end of page? + printFooter(g); // print page footer + currentHeight = nullHeight; // set next page top + return; // stop rendering + } + else { // still within page + nullHeight = symbol.top; // save last null height + for (sym = 0; sym < nextSymbol; sym++) { // go through symbols + symbol = symbolBuff[sym]; // get stored symbol + symbol.top += offset; // move symbol up page + symbol.draw(g); // draw symbol + symbol.top -= offset; // move symbol back + } + nextSymbol = 0; // back to buffer start + } + } + else if (symbol.top >= currentHeight) { // inside current page? + if (nextSymbol < maxSymbols) // room for symbol? + symbolBuff[nextSymbol++] = symbol; // store symbol + else { // no room for symbol + System.err.println( // report error + "Cannot print more than " + maxSymbols + " symbols"); + System.exit(1); // leave programme + } + } + } + if (symbol != null && // non-null symbol? + symbol.top >= nextHeight) // past end of page? + currentHeight = nullHeight; // set next page top + else { // still within page + for (sym = 0; sym < nextSymbol; sym++) { // go through symbols + symbol = symbolBuff[sym]; // get stored symbol + symbol.top += offset; // move symbol up page + symbol.draw(g); // draw symbol + symbol.top -= offset; // move symbol back + } + System.err.println(); // end page listing + stopped = true; // printing finished + } + printFooter(g); // print page footer + } + + /** + Print footer of time sequence diagram. + + @param g graphics object + */ + public void printFooter(Graphics g) { // print page footer + String footer = // set page footer + ProtocolSimulator.protocolType + " Page " + page; + FontMetrics fm = g.getFontMetrics(footerFont); // get font metrics + int diagWidth = getSize().width; // diagram width + int footerWidth = fm.stringWidth(footer); // get footer width + g.drawString(ProtocolSimulator.protocolType + // print page footer + " Page " + page, + margin + (diagWidth - footerWidth) / 2, inset + maxHeight); + } + + /** + Note printing as started. + */ + public void printStart() { // start printing + starting = true; // set not started + } + + /** + Check if printing has stopped. + + @return true/false if printing has/has not stopped + */ + public boolean printStopped() { // return print status + return((stopped)); // get printing stopped + } + +} + diff --git a/lab8_protocols/jasper/source/simulator/TransmissionSymbol.java b/lab8_protocols/jasper/source/simulator/TransmissionSymbol.java new file mode 100755 index 0000000..2ca6f1d --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/TransmissionSymbol.java @@ -0,0 +1,83 @@ +// TransmissionSymbol.java + +package simulator; + +import java.awt.*; +import support.ProtocolEvent; + +/** + This is the class for a general transmission symbol. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.5 (22nd July 2010, KJT): minor tidying +*/ +public abstract class TransmissionSymbol extends TSDSymbol { + + /** Left-to-right (if true, else right-to-left) */ + protected boolean lToR; + + /** Dash length */ + protected int dashLength = 6; + + /** + Constructor for a transmission symbol + + @param event protocol event + @param lToR left-to-right (if true, else right-to-left) + @param col column + @param top top of service primitive + @param height height of successful transmission + */ + public TransmissionSymbol( + ProtocolEvent event, boolean lToR, int col, int top, int height) { + super(event, col, top); + this.lToR = lToR; + this.height = height; + } + + /** + Draw a dashed line from (x1, y1) to (x2, y2) using the given dash length for + the lines and graps of the dash. + + @param g graphics object + @param x1 initial X-coordinate + @param y1 initial Y-coordinate + @param x2 final X-coordinate + @param y2 final Y-coordinate + @param dashLength dash length + */ + protected void drawDashedLine( + Graphics g, int x1, int y1, int x2, int y2, int dashLength) { + float x0 = x1; + float y0 = y1; + float x = 0.0f; + float y = 0.0f; + float s = 0.0f; + int deltaX = x2 - x1; + int deltaY = y2 - y1; + float length = (float) (Math.sqrt(deltaX * deltaX + deltaY * deltaY)); + float dx = dashLength * deltaX / length; + float dy = dashLength * deltaY / length; + x += dx / 2; + y += dy / 2; + float ds = (float) (Math.sqrt(dx * dx + dy * dy)); + while (s <= length - ds) { + g.drawLine( + Math.round(x + x0), Math.round(y + y0), + Math.round(x + x0 + dx), Math.round(y + y0 + dy)); + x += 2 * dx; + y += 2 * dy; + s += 2 * ds; + } + } + + /** + Empty draw method to be overridden. + + @param g graphics object + */ + public abstract void draw(Graphics g); + +} + diff --git a/lab8_protocols/jasper/source/simulator/TraverseTransmission.java b/lab8_protocols/jasper/source/simulator/TraverseTransmission.java new file mode 100755 index 0000000..20946d9 --- /dev/null +++ b/lab8_protocols/jasper/source/simulator/TraverseTransmission.java @@ -0,0 +1,55 @@ +// TraverseTransmission.java + +package simulator; + +import java.awt.*; // import Java AWT classes + +import support.*; // import Jasper protocol event + +/** + This is the class for a traverse transmission arrow. + + @author Kenneth J. Turner + @version 1.5 (22nd July 2010, KJT): minor tidying +*/ +public class TraverseTransmission extends TransmissionSymbol { + + /** Number of columns to traverse */ + private int columns; + + /** + Constructor for a traverse transmission arrow. + + @param event protocol event + @param lToR true = left-to-right + @param source source column + @param columns columns + @param top top of service primitive + @param height height of successful transmission + */ + public TraverseTransmission( + ProtocolEvent event, boolean lToR, int source, int columns, int top, + int height) { + super(event, lToR, source, top, height); + this.columns = columns; + } + + /** + Paint the traverse transmission as a dashed line representing transmission + across the intervening columns. + + @param g graphics object + */ + public void draw(Graphics g) { + int leftX = inset + col * columnWidth + arrowWidth / 2; + int rightX = leftX + columns*columnWidth - arrowWidth; + g.setColor(Color.blue); + if (lToR) + drawDashedLine(g, leftX, top, rightX, top + height, dashLength); + else + drawDashedLine(g, rightX, top, leftX, top + height, dashLength); + g.setColor(Color.black); + } + +} + diff --git a/lab8_protocols/jasper/source/support/Medium.java b/lab8_protocols/jasper/source/support/Medium.java new file mode 100755 index 0000000..cfe8965 --- /dev/null +++ b/lab8_protocols/jasper/source/support/Medium.java @@ -0,0 +1,258 @@ +// Medium.java + +package support; // protocol support package + +import java.util.Enumeration; // import Java enumerations +import java.util.HashSet; // import Java hash sets +import java.util.StringTokenizer; // import Java string tokenisers +import java.util.Vector; // import Java vectors + +/** + This is the class for a protocol medium. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version
+
1.4 (9th March 2006, KJT): updated for JDK 1.5, use of Swing + graphics, minor tidying +
1.5 (27th July 2010, KJT): minor tidying; "isEmpty" check added +*/ +public class Medium implements ProtocolEntity { + + /** Control delivery and loss */ + public final static int CONTROL_DELIVERY_LOSS = 0; + + /** Control delivery (but not loss) */ + public final static int CONTROL_DELIVERY = 1; + + /** Control neither delivery nor loss - immediately deliver on reception */ + public final static int CONTROL_NOTHING = 2; + + /** PDUs in the medium */ + protected Vector pdus; + + /** Destination events */ + protected Vector destinationEvents; + + /** Medium control type */ + private int mediumType = CONTROL_DELIVERY_LOSS; + + /** + Constructor for a medium. + */ + public Medium() { + initialise(); + } + + /** + Get the name attribute of a medium. + + @return The name value + */ + public String getName() { + return("Medium"); + } + + /** + Identify and return PDU currently on the medium with type and parameters + matching those in the given description. + + @param description service description + @return corresponding PDU (or null if not found) + */ + + protected PDU getMatchingPDU(String description) { + PDU pdu; + int seq = -1; + // extract name of source entity for this PDU: + int sourceStart = description.indexOf('[') + 1; + int sourceEnd = description.indexOf(']'); + String sourceName = description.substring(sourceStart, sourceEnd); + // get type and parameters of PDU from action string + // PDU type starts after first space following "Deliver" or "Lose" + int typeStart = description.indexOf(' ') + 1; + int typeEnd = description.indexOf('('); + if (typeEnd < 0) + typeEnd = sourceStart - 2; + String type = description.substring(typeStart, typeEnd); + String[] params = getParams(description); + boolean noParams = params == null; + int paramCount = noParams ? 0 : params.length; + if (paramCount == 1) + seq = Integer.parseInt(params[0]); // sequence number + // try to match PDU type and seq with a PDU currently on channel + for (Enumeration enumeration = pdus.elements(); + enumeration.hasMoreElements(); ) { + pdu = (PDU) enumeration.nextElement(); + boolean matches = false; + if (pdu != null && pdu.type.equals(type) + && pdu.getSource().getName().equals(sourceName)) { + if (noParams) + matches = true; + else if (seq >= 0) + matches = pdu.seq == seq; + } + if (matches) + return (pdu); + } + return (null); // no match found + } + + /** + Extract comma-separated parameter strings within round brackets in string + argument. + + @param description description + @return comma-separated parameters + */ + protected String[] getParams(String description) { + int openBracket = description.indexOf("("); + if (openBracket >= 0) { // check for opening bracket + int closeBracket = description.indexOf(")"); + String s1 = description.substring(openBracket + 1, closeBracket); + StringTokenizer st = new StringTokenizer(s1, ","); + String[] result = new String[st.countTokens()]; + for (int i = 0; st.hasMoreTokens(); i++) + result[i] = st.nextToken(); + return (result); + } + return (null); + } + + /** + Return a list of strings describing actions (services) that can be carried + out in the current state. + + @return service descriptions + */ + public Vector getServices() { + Vector list = new Vector(); // initialise service list + HashSet pdusSent = // initialise list of sources + new HashSet(); + for (Enumeration enumeration = pdus.elements(); // go through PDUs + enumeration.hasMoreElements(); ) { + PDU pdu = (PDU) enumeration.nextElement();// get next PDU + if (pdu != null) { // non-null PDU? + String name = pdu.getSource().getName();// get PDU source name + String description = // get PDU id and name + pdu.getID() + " [" + name + "]"; + if (!pdusSent.contains(name)) { // no PDU with this source? + pdusSent.add(name); // add source to list + list.addElement( // add delivery service + "Deliver " + description + " - no loss"); + } + if (mediumType == CONTROL_DELIVERY_LOSS)// delivery and loss controlled? + list.addElement( // add loss service + "Lose " + description + " - congestion/error"); + } + } + return(list); // return service list + } + + /** + Initialise the medium. + */ + public void initialise() { + pdus = new Vector(); + destinationEvents = new Vector(); + } + + /** + Check if the medium is empty (i.e. has no PDUs in either direction). + + @return true/false if the medium has no/has PDUs + */ + public boolean isEmpty() { + return(pdus == null || pdus.size() == 0); // return empty check + } + + /** + Check if the medium is empty (i.e. has no PDUs with the given source name). + + @param entityName entity name + @return true/false if the medium has no/has PDUs + */ + public boolean isEmpty(String entityName) { + for (Enumeration enumeration = pdus.elements(); // go through PDUs + enumeration.hasMoreElements(); ) { + PDU pdu = (PDU) enumeration.nextElement();// get next PDU + String name = pdu.getSource().getName(); // get PDU source name + if (name.equals(entityName)) // PDU with given source? + return(false); // immediately return false + } + return(true); // return true as no PDUs + } + + /** + Carry out the action specified in the given string, and return a list of + protocol events resulting from this action + + @param service service to perform + @return resulting protocol events + */ + + public Vector performService(String service) { + Vector events = new Vector(); + destinationEvents.removeAllElements(); + PDU pdu = getMatchingPDU(service); + if (pdu != null) { + if (service.startsWith("Deliver")) { + transmitPDU(pdu, pdu.getDestination()); + pdus.removeElement(pdu); + events.addElement(new ProtocolEvent(ProtocolEvent.RECEIVE, pdu)); + } + else if (service.startsWith("Lose")) { + destinationEvents.removeAllElements(); + pdus.removeElement(pdu); + events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, pdu)); + } + } + // tack on any events triggered by delivery of a message + // to its destination protocol entity + for (Enumeration enumeration = destinationEvents.elements(); + enumeration.hasMoreElements(); ) + events.addElement((ProtocolEvent) enumeration.nextElement()); + return(events); + } + + /** + Passively accept an incoming PDU and add to the list of those currently on + channel. + + @param pdu PDU + @return resulting protocol events + */ + public Vector receivePDU(PDU pdu) { + Vector events = new Vector(); + pdus.addElement(pdu); + if (mediumType == CONTROL_NOTHING) { + String s = "Deliver " + pdu.getID() + + " [" + pdu.getSource().getName() + "] - no loss"; + events.addAll(performService(s)); + transmitPDU(pdu, pdu.getDestination()); + } + return(events); // return empty list of events + } + + /** + Set user control over delivery and loss. + + @param mediumType medium control type + */ + public void setMediumType(int mediumType) { + this.mediumType = mediumType; + } + + /** + Deliver specified PDU to destination protocol entity, and get any protocol + events triggered by this delivery. + + @param pdu PDU + @param destination PDU destination + */ + + public void transmitPDU(PDU pdu, ProtocolEntity destination) { + destinationEvents = destination.receivePDU(pdu); + } + +} + diff --git a/lab8_protocols/jasper/source/support/PDU.java b/lab8_protocols/jasper/source/support/PDU.java new file mode 100755 index 0000000..3435e5c --- /dev/null +++ b/lab8_protocols/jasper/source/support/PDU.java @@ -0,0 +1,184 @@ +// PDU.java + +package support; // protocol support package + +/** + This is the main class for protocol simulator. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (22nd July 2010, KJT): commenting and minor tidying +*/ +public class PDU { + + /** Protocol Data Unit type */ + public String type = ""; + + /** Protocol Data Unit sequence number (-1 = none) */ + public int seq = -1; + + /** Protocol Data Unit user data length */ + public int size = 0; + + /** Protocol Data Unit payload as Service Data Unit */ + public String sdu = ""; + + /** Protocol Data Unit source */ + public ProtocolEntity source; + + /** Protocol Data Unit destination */ + public ProtocolEntity destination; + + /** + Constructor for a PDU object. + */ + public PDU() { + } + + /** + Constructor for a PDU object. + + @param type PDU type + */ + public PDU(String type) { + this.type = type; + } + + /** + Constructor for a PDU object. + + @param type PDU type + @param seq PDU sequence number + */ + public PDU(String type, int seq) { + this.type = type; + this.seq = seq; + } + + /** + Constructor for a PDU object. + + @param type PDU type + @param sdu PDU payload as SDU + */ + public PDU(String type, String sdu) { + this.type = type; + this.sdu = sdu; + } + + /** + Constructor for a PDU object. + + @param type PDU type + @param seq PDU sequence number + @param sdu PDU payload as SDU + */ + public PDU(String type, int seq, String sdu) { + this.type = type; + this.seq = seq; + this.sdu = sdu; + } + + /** + Get the destination attribute of a PDU object. + + @return PDU destination + */ + public ProtocolEntity getDestination() { + return(destination); + } + + /** + Get the identifier attribute of a PDU object. + + @return PDU identifier (i.e. label) + */ + public String getID() { + return(getLabel()); + } + + /** + Get the label attribute of the PDU object, identifying an arrow in a time + sequence diagram. + + @return PDU label + */ + public String getLabel() { + String label = type; // parameter if any in brackets + if (seq >= 0) + label += "(" + seq + ")"; // show seq number parameter + else if (!sdu.equals("")) + label += "(" + sdu + ")"; // show SDU parameter + return(label); + } + + /** + Get the SDU attribute of a PDU object. + + @return PDU payload as SDU + */ + public String getSDU() { + return(sdu); + } + + /** + Get the source attribute of a PDU object. + + @return The source value + */ + public ProtocolEntity getSource() { + return(source); + } + + /** + Check whether a PDU matches this one. + + @param pdu PDU to be checked + @return true/false if PDU matches/does not match this one + */ + public boolean matches(PDU pdu) { + if (pdu == null) + return(false); + return( + type.equals(pdu.type) && source == pdu.getSource() && seq == pdu.seq); + } + + /** + Sets the destination attribute of a PDU object. + + @param destination PDU destination + */ + public void setDestination(ProtocolEntity destination) { + this.destination = destination; + } + + /** + Sets the SDU attribute of athe PDU object. + * + @param sdu PDU payload as SDU + */ + public void setSDU(String sdu) { + this.sdu = sdu; + } + + /** + Set the source attribute of a PDU object. + + @param source PDU source + */ + public void setSource(ProtocolEntity source) { + this.source = source; + } + + /** + Convert a PDU to a string representation. + + @return string representation of a PDU + */ + public String toString() { + return ("PDU "); + } + +} + diff --git a/lab8_protocols/jasper/source/support/Protocol.java b/lab8_protocols/jasper/source/support/Protocol.java new file mode 100755 index 0000000..71382a8 --- /dev/null +++ b/lab8_protocols/jasper/source/support/Protocol.java @@ -0,0 +1,151 @@ +// Protocol.java + +package support; // protocol support package + +import java.util.*; // import Java utility classes + +/** + This is the base class for protocols. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (27th July 2010, KJT): minor tidying +*/ +public abstract class Protocol { + + /** Protocol entity list */ + protected Vector entities; + + /** Protocol medium */ + protected Medium medium; + + /** + Return the protocol entity list + + @return protocol entities + */ + public Vector getEntities() { + return(entities); + } + + /** + Return the column used by the medium. + + @return medium column number + */ + public int getMediumColumn() { + return(entities.indexOf(medium)); + } + + /** + Return the random numbers maintained by a protocol. By default this does + nothing, but can be overriden by particular protocols. The numbers are + stored in a scenario file. + + @return random number list + */ + public Vector getRandomNumbers() { + return(null); + } + + /** + Return the services offered by a protocol (normally overriden by an actual + protocol). + + @return The services value + */ + public Vector getServices() { + Vector list = new Vector(); + for (Enumeration ee = entities.elements(); ee.hasMoreElements(); ) { + ProtocolEntity entity = (ProtocolEntity) ee.nextElement(); + for (Enumeration se = entity.getServices().elements(); + se.hasMoreElements(); ) + list.addElement(entity.getName() + ": " + (String) se.nextElement()); + } + return(list); + } + + /** + Initialise all protocol entities. + */ + public void init() { + for (Enumeration enumeration = entities.elements(); + enumeration.hasMoreElements(); ) { + ProtocolEntity entity = (ProtocolEntity) enumeration.nextElement(); + entity.initialise(); + } + } + + /** + Perform service specified by the action string. This consists of an entity + name and a service. First, identify the entity providing the service. + + @param action action + @return resulting protocol events + */ + public Vector performService(String action) { + for (Enumeration en = entities.elements(); en.hasMoreElements(); ) { + ProtocolEntity entity = (ProtocolEntity) en.nextElement(); + String name = entity.getName(); + if (action.startsWith(name)) { + String service = // remove entity name and ":" + action.substring(name.length() + 2); + Vector events = // service string to entity + entity.performService(service); + // check if any event contains a PDU whose timer needs to be enabled + for (Enumeration enumeration = events.elements(); + enumeration.hasMoreElements(); ) { + ProtocolEvent event = (ProtocolEvent) enumeration.nextElement(); + if (event == null) + break; + PDU pdu = event.getPDU(); + if (pdu == null) + break; + ProtocolEntity source = pdu.getSource(); + if (source != null && source instanceof Timeouts) { + Timeouts sender = (Timeouts) source; + if (sender.hasTimer(pdu.type)) { // this PDU type has a timer + sender.setTimer(pdu, entity == medium); + break; // don't time response PDUs + } + } + } + return(events); + } + } + return(null); // entity name not recognised + } + + /** + This method is called by the protocol simulator. Override this method to + allow protocol-specific parameters to be set externally (e.g. from + JavaScript). The standard support is for a medium type. + + @param parameter parameter name + @param value parameter value + */ + public void setParameter(String parameter, String value) { + if (parameter.equals("mediumType")) + try { + medium.setMediumType(Integer.parseInt(value)); + } + catch (NumberFormatException exception) { // invalid numeric value + System.out.println("number format exception for parameter '" + + parameter + "' with value '" + value + "'"); + } + } + + /** + Set the random numbers maintained by a protocol. By default this does + nothing, but can be overriden by particular protocols. The numbers are + retrieved from a scenario file. + + @param randoms random number list + */ + public void setRandomNumbers(Vector randoms) { + } + +} + + diff --git a/lab8_protocols/jasper/source/support/ProtocolEntity.java b/lab8_protocols/jasper/source/support/ProtocolEntity.java new file mode 100755 index 0000000..a292607 --- /dev/null +++ b/lab8_protocols/jasper/source/support/ProtocolEntity.java @@ -0,0 +1,29 @@ +// ProtocolEntity.java + +package support; // Jasper support package + +import java.util.Vector; // import Java vectors + +/** + This is the base interface for protocol entities. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (22nd July 2010, KJT): minor tidying +*/ +public interface ProtocolEntity { + + public String getName(); + + public Vector getServices(); + + public void initialise(); + + public Vector performService(String s); + + public Vector receivePDU(PDU pdu); + + public void transmitPDU(PDU pdu, ProtocolEntity dest); + +} diff --git a/lab8_protocols/jasper/source/support/ProtocolEvent.java b/lab8_protocols/jasper/source/support/ProtocolEvent.java new file mode 100755 index 0000000..8b72717 --- /dev/null +++ b/lab8_protocols/jasper/source/support/ProtocolEvent.java @@ -0,0 +1,128 @@ +// ProtocolEvent.java + +package support; // protocol support package + +/** + This is the class for protocol events. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.4 (9th March 2006, KJT): updated for JDK 1.5 +
1.5 (22nd July 2010, KJT): minor tidying; addition of TRAVERSE + event +*/ +public class ProtocolEvent { + + /** Protocol event user -> protocol */ + public final static int SEND = 0; + + /** Protocol event protocol -> user */ + public final static int DELIVER = 1; + + /** Protocol event protocol -> medium */ + public final static int TRANSMIT = 2; + + /** Protocol event medium -> protocol */ + public final static int RECEIVE = 3; + + /** Protocol event medium fragmentation */ + public final static int FRAGMENT = 4; + + /** Protocol event medium loss */ + public final static int LOSE = 5; + + /** Protocol event timeout and retransmission */ + public final static int TIMEOUT = 6; + + /** Protocol event arbitrary comment */ + public final static int COMMENT = 7; + + /** Protocol event entity -> entity */ + public final static int TRAVERSE = 8; + + /** Protocol event protocol data unit */ + private PDU pdu = null; + + /** Protocol event type */ + private int eventType; + + /** Protocol event comment */ + private String comment = ""; + + /** Protocol event target */ + private ProtocolEntity target; + + /** + Constructor for a protocol event. + + @param type event type + @param pdu protocol data unit + */ + public ProtocolEvent(int type, PDU pdu) { + eventType = type; + this.pdu = pdu; + } + + /** + Constructor for a protocol event. + + @param type event type + @param target target entity + @param comment event comment + */ + public ProtocolEvent(int type, ProtocolEntity target, String comment) { + eventType = type; + this.target = target; + this.comment = comment; + } + + /** + Get the comment attribute of a protocol event. + + @return comment + */ + public String getComment() { + return(comment); + } + + /** + Get the PDU attribute of a protocol event + + @return The pDU value + */ + public PDU getPDU() { + return(pdu); + } + + /** + Get the target attribute of a protocol event. + + @return target + */ + public ProtocolEntity getTarget() { + return(target); + } + + /** + Get the type attribute of a protocol event. + + @return The type value + */ + public int getType() { + return(eventType); + } + + /** + Convert to a string representation of a protocol event. + + @return A string representation of the object. + */ + public String toString() { + return( + "Event "); + } + +} + diff --git a/lab8_protocols/jasper/source/support/Timeouts.java b/lab8_protocols/jasper/source/support/Timeouts.java new file mode 100755 index 0000000..45a8545 --- /dev/null +++ b/lab8_protocols/jasper/source/support/Timeouts.java @@ -0,0 +1,28 @@ +// Timeouts.java + +package support; // protocol support package + +/** + This is the base interface for timeouts, implemented by protocols that need + them. + + @author Iain A. Robin, Kenneth J. Turner + @version 1.0 (1st September 1999, IAR): initial version +
1.5 (27th July 2010, KJT): minor tidying +*/ +public interface Timeouts { + + /** + Report whether a specified PDU type has an associated timer. + */ + public boolean hasTimer(String type); + + /** + Set the timer for a specified PDU. + + @param pdu PDU + @param enabled whether the timer is enabled + */ + public void setTimer(PDU pdu, boolean enabled); + +} diff --git a/lab9_processes/Lab Assignment - Processes.pdf b/lab9_processes/Lab Assignment - Processes.pdf new file mode 100644 index 0000000..3279068 Binary files /dev/null and b/lab9_processes/Lab Assignment - Processes.pdf differ diff --git a/lab9_processes/assignmentcode.java b/lab9_processes/assignmentcode.java new file mode 100644 index 0000000..6450816 --- /dev/null +++ b/lab9_processes/assignmentcode.java @@ -0,0 +1,63 @@ +/**Perhaps the easiest technique for implementing a shell interface is to have the program first read what the user enters on the command line (here, cat Prog.java) and then have the program create a separate external process that performs the command. You can create the separate process using the code provided below:**/ + +import java.io.*; + +public class AProcess +{ + public static void main(String[] args) throws IOException { + if (args.length != 1) { + System.err.println("Usage: java OSProcess "); + System.exit(0); + } + + // args[0] is the command + ProcessBuilder pb = new ProcessBuilder(args[0]); + Process process = pb.start(); + + // obtain the input and output streams + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while ( (line = br.readLine()) != null) + System.out.println(line); + + br.close(); + } +} + + +/**The following code contains the basic operations of a command-line shell. The main() method presents the prompt ‘prompt>’ and waits to read input from the user. The program is terminated when the user enters .**/ + + +import java.io.*; + +public class ABasicShell +{ + public static void main(String[] args) throws java.io.IOException { + String commandLine; + BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); + + // we break out with + while (true) { + // read what they entered + System.out.print("prompt>"); + commandLine = console.readLine(); + + // if they entered a return, just loop again + if (commandLine.equals("")) + continue; + + /** + The basic steps are: + (1) parse the input to obtain the command + and any parameters + (2) create a ProcessBuilder object + (3) start the process + (4) obtain the output stream + (5) output the contents returned by the command + */ + } + } +} diff --git a/lab9_processes/src/ABasicShell.class b/lab9_processes/src/ABasicShell.class new file mode 100644 index 0000000..10a40b4 Binary files /dev/null and b/lab9_processes/src/ABasicShell.class differ diff --git a/lab9_processes/src/ABasicShell.java b/lab9_processes/src/ABasicShell.java new file mode 100644 index 0000000..b3d6513 --- /dev/null +++ b/lab9_processes/src/ABasicShell.java @@ -0,0 +1,38 @@ +import java.io.*; + +public class ABasicShell +{ + public static void main(String[] args) throws java.io.IOException { + String commandLine; + BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); + + // we break out with + while (true) { + // read what they entered + System.out.print("prompt>"); + commandLine = console.readLine(); + + // if they entered a return, just loop again + if (!commandLine.equals("")){ + AProcess aProcess = new AProcess(); + + String[] mycommands = commandLine.split(" "); + + aProcess.main(mycommands); + + } else{ + continue; + } + + /** + The basic steps are: + (1) parse the input to obtain the command + and any parameters + (2) create a ProcessBuilder object + (3) start the process + (4) obtain the output stream + (5) output the contents returned by the command + */ + } + } +} diff --git a/lab9_processes/src/AProcess.class b/lab9_processes/src/AProcess.class new file mode 100644 index 0000000..7b2d0e1 Binary files /dev/null and b/lab9_processes/src/AProcess.class differ diff --git a/lab9_processes/src/AProcess.java b/lab9_processes/src/AProcess.java new file mode 100644 index 0000000..4dde25a --- /dev/null +++ b/lab9_processes/src/AProcess.java @@ -0,0 +1,25 @@ +import java.io.*; + +public class AProcess { + public static void main(String[] args) throws IOException { + //if (args.length != 1) { + // System.err.println("Usage: java OSProcess "); + // System.exit(0); + //} + + // args is the command + ProcessBuilder pb = new ProcessBuilder(args); + Process process = pb.start(); + + // obtain the input and output streams + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while ( (line = br.readLine()) != null) + System.out.println(line); + + br.close(); + } +}