3 * Time-stamp: "2007-11-06 18:18:06 anton"
6 // import necessary input/output classes
7 import java
.io
.BufferedWriter
;
8 import java
.io
.DataOutputStream
;
9 import java
.io
.FileWriter
;
10 import java
.io
.IOException
;
11 import java
.io
.InputStream
;
13 // import necessary network classes
14 import java
.net
.ConnectException
;
15 import java
.net
.DatagramPacket
;
16 import java
.net
.InetAddress
;
17 import java
.net
.ServerSocket
;
18 import java
.net
.Socket
;
19 import java
.net
.UnknownHostException
;
22 * TCPNode – a node in a TCP packet ring.
24 * @author "Anton Johansson" <anton.johansson@gmail.com>
25 * @author "Victor Zamanian" <victor.zamanian@gmail.com>
27 public class TCPNode
extends Node
{
29 private ServerSocket welcomeSocket
;
31 private Socket nextNode
;
32 private Socket prevNode
;
34 private DataOutputStream nextNodeOutput
;
35 private InputStream prevNodeInput
;
38 * Creates a new TCPNode instance with all necessary information.
40 * @param localPort the local port to listen to
41 * @param nextHost the host to connect to
42 * @param nextPort the port that <code>nextHost</code> listens to
43 * @param logger determines if this node should log lap-times
45 public TCPNode(int localPort
,
50 super(localPort
, nextHost
, nextPort
, logger
);
53 final int MAXTRIES
= 120;
55 // create a server socket so a node can connect to this one
57 welcomeSocket
= new ServerSocket(localPort
);
59 catch (IOException e
) {
63 // first try to make contact with
64 // the next and previous node in the ring
66 nextNode
= new Socket(nextHost
, nextPort
);
67 prevNode
= welcomeSocket
.accept();
69 catch (IOException e
) {
70 System
.out
.println(e
);
73 // if connecting to the next node failed
74 if (nextNode
== null) {
75 // try to let the previous node connect to this node
77 prevNode
= welcomeSocket
.accept();
79 catch (IOException e
) {
83 // then try to connect to the next node a number of times
84 while (nextNode
== null && tries
< MAXTRIES
) {
86 nextNode
= new Socket(nextHost
, nextPort
);
88 catch (IOException e
) {
89 System
.out
.println(e
);
91 // increase the number of tries made by 1
96 catch (InterruptedException e
) {
102 // if we after all of these efforts have been unsuccessful
103 // in connecting to the next and previous node, exit
104 // with a status code of 1, indicating failure.
105 if (nextNode
== null || prevNode
== null) {
106 System
.out
.println("Unable to connect; quitting...");
111 nextNodeOutput
= new DataOutputStream(
112 nextNode
.getOutputStream());
113 prevNodeInput
= prevNode
.getInputStream();
115 catch (IOException e
) {
120 // if this node is a logger, set the file name
121 // and open a file with that name for writing.
123 logFileName
= "lap-times-tcp.log";
125 file
= new FileWriter(logFileName
);
126 out
= new BufferedWriter(file
);
128 catch (IOException e
) {
135 * Runs the main logic of the TCPNode
137 public void runTCPNode() {
138 // the byte-array to store received data in
139 byte[] receivedData
= new byte[DATAGRAMSIZE
];
140 // the current phase the ring is in
143 //send the first packet
145 nextNodeOutput
.write(constructByteArray(
146 FINDMASTER
, new Integer(ID
).toString()));
148 catch (IOException e
) {
154 while (phase
== FINDMASTER
) {
157 prevNodeInput
.read(receivedData
);
159 catch (IOException e
) {
163 // acquire the message's message type
164 // also set the phase to the message type, because
165 // if it changes (to RUNMODE), that means another
166 // node was chosen as the master node.
167 int messageType
= phase
= receivedData
[0];
169 // if the message received was a message of type RUNMODE
170 if (phase
== RUNMODE
) {
171 // send a message to the following node(s),
172 // telling it to break out of phase one.
174 nextNodeOutput
.write(receivedData
);
177 catch (IOException e
) {
180 // if the start time hasn't been set
181 if (isLogger
&& startTime
== 0) {
182 // set the starting time -- the time
183 // when phase two was initiated
184 startTime
= System
.nanoTime();
186 // break out of phase one
190 // acquire the message's length
191 int messageLength
= receivedData
[1];
193 // acquire the message...
195 new String(receivedData
).substring(2, messageLength
+ 2);
197 // the largest ID that this packet has seen so far, i.e. the
198 // ID of the node with the largest ID so far in the ring.
199 int receivedID
= Integer
.parseInt(message
);
201 // if this node will surely not be the master node because
202 // there exists a node with an ID larger than this one's
203 if (receivedID
> ID
) {
204 // just forward the data to the next node
206 nextNodeOutput
.write(receivedData
);
208 catch (IOException e
) {
213 // if this node should be the master node
214 else if (receivedID
== ID
) {
215 // set this node to be the master node
217 // set the phase to RUNMODE
222 // if this node turned out to be the master node,
223 // send the first message of phase two.
225 // send first packet in phase two
227 nextNodeOutput
.write(constructByteArray(
228 RUNMODE
, new Integer(ID
).toString()));
229 // set the start time if it hasn't been set
230 if (isLogger
&& startTime
== 0) {
231 startTime
= System
.nanoTime();
234 catch (IOException e
) {
238 // increase the amount of sent packets by 1
243 while (phase
== RUNMODE
) {
244 // receive data from previous node
246 prevNodeInput
.read(receivedData
);
248 catch (IOException e
) {
252 // if this node is a logging node,
254 // write the current average lap time of the data
255 writeToLog(new String(receivedData
).substring(2, receivedData
[1] + 2));
258 // send data to the next node
260 nextNodeOutput
.write(receivedData
);
262 catch (IOException e
) {
267 // if it is time to end phase two
268 if (sentPackets
== PACKETSTOSEND
) {
269 // set the phase to phase three
270 // and send a terminating packet
274 // send terminate packet
275 nextNodeOutput
.write(
276 constructByteArray(TERMINATE
, null));
277 // receive terminate packet before
278 // exiting the ring by closing socket
279 prevNodeInput
.read(receivedData
);
281 catch (IOException e
) {
288 // close sockets (and log file if this node is a logger)
293 System
.out
.println("Closing log file...");
295 System
.out
.println("Finished.");
298 catch (IOException e
) {
304 * This method is not used...
306 * @param receivePacket
309 public boolean receivePacket(DatagramPacket receivePacket
) {
310 return false; // not used, but interfaces can be demanding...
314 * This method is not used...
316 * @param receivePacket
319 public boolean sendPacket(DatagramPacket receivePacket
) {
320 return false; // not used, but interfaces can be demanding...
324 * For starting the TCPNode from a commandline
326 * @param args should contain local-port next-host next-port [L]
327 * where the L is optional and specifies if the node
328 * should log lap-times
329 * @exception IOException if an error occurs
331 public static void main(String
[] args
) throws IOException
,
332 InterruptedException
{
334 // determine if this node should be a logger
335 boolean logger
= false;
336 if (args
.length
== 4 && args
[3].charAt(0) == 'L') {
341 node
= new TCPNode(Integer
.parseInt(args
[0]),
342 InetAddress
.getByName(args
[1]),
343 Integer
.parseInt(args
[2]),