Merge branch 'master' of git://repo.or.cz/packetlaptime
[packetlaptime.git] / TCPNode.java
bloba63b452bcf658440a35324e64e8f92e70fd556ff
1 /*
2 * @(#)TCPNode.java
3 * Time-stamp: "2007-11-06 18:18:06 anton"
4 */
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;
21 /**
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;
37 /**
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,
46 InetAddress nextHost,
47 int nextPort,
48 boolean logger) {
50 super(localPort, nextHost, nextPort, logger);
52 int tries = 0;
53 final int MAXTRIES = 120;
55 // create a server socket so a node can connect to this one
56 try {
57 welcomeSocket = new ServerSocket(localPort);
59 catch (IOException e) {
60 e.printStackTrace();
63 // first try to make contact with
64 // the next and previous node in the ring
65 try {
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
76 try {
77 prevNode = welcomeSocket.accept();
79 catch (IOException e) {
80 e.printStackTrace();
83 // then try to connect to the next node a number of times
84 while (nextNode == null && tries < MAXTRIES) {
85 try {
86 nextNode = new Socket(nextHost, nextPort);
88 catch (IOException e) {
89 System.out.println(e);
91 // increase the number of tries made by 1
92 tries++;
93 try {
94 Thread.sleep(250);
96 catch (InterruptedException e) {
97 e.printStackTrace();
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...");
107 System.exit(1);
109 else {
110 try {
111 nextNodeOutput = new DataOutputStream(
112 nextNode.getOutputStream());
113 prevNodeInput = prevNode.getInputStream();
115 catch (IOException e) {
116 e.printStackTrace();
120 // if this node is a logger, set the file name
121 // and open a file with that name for writing.
122 if (isLogger) {
123 logFileName = "lap-times-tcp.log";
124 try {
125 file = new FileWriter(logFileName);
126 out = new BufferedWriter(file);
128 catch (IOException e) {
129 e.printStackTrace();
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
141 int phase;
143 //send the first packet
144 try {
145 nextNodeOutput.write(constructByteArray(
146 FINDMASTER, new Integer(ID).toString()));
148 catch (IOException e) {
149 e.printStackTrace();
152 // PHASE ONE
153 phase = FINDMASTER;
154 while (phase == FINDMASTER) {
155 // receive packet
156 try {
157 prevNodeInput.read(receivedData);
159 catch (IOException e) {
160 e.printStackTrace();
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.
173 try {
174 nextNodeOutput.write(receivedData);
175 sentPackets++;
177 catch (IOException e) {
178 e.printStackTrace();
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
187 break;
190 // acquire the message's length
191 int messageLength = receivedData[1];
193 // acquire the message...
194 String 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
205 try {
206 nextNodeOutput.write(receivedData);
208 catch (IOException e) {
209 e.printStackTrace();
213 // if this node should be the master node
214 else if (receivedID == ID) {
215 // set this node to be the master node
216 isMasterNode = true;
217 // set the phase to RUNMODE
218 phase = RUNMODE;
220 } // end while
222 // if this node turned out to be the master node,
223 // send the first message of phase two.
224 if (isMasterNode) {
225 // send first packet in phase two
226 try {
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) {
235 e.printStackTrace();
238 // increase the amount of sent packets by 1
239 sentPackets++;
242 // PHASE TWO
243 while (phase == RUNMODE) {
244 // receive data from previous node
245 try {
246 prevNodeInput.read(receivedData);
248 catch (IOException e) {
249 e.printStackTrace();
252 // if this node is a logging node,
253 if (isLogger) {
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
259 try {
260 nextNodeOutput.write(receivedData);
262 catch (IOException e) {
263 e.printStackTrace();
265 sentPackets++;
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
271 phase = TERMINATE;
272 if (isMasterNode) {
273 try {
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) {
282 e.printStackTrace();
286 } // end while
288 // close sockets (and log file if this node is a logger)
289 try {
290 nextNode.close();
291 prevNode.close();
292 if (isLogger) {
293 System.out.println("Closing log file...");
294 closeLogFile();
295 System.out.println("Finished.");
298 catch (IOException e) {
299 e.printStackTrace();
304 * This method is not used...
306 * @param receivePacket
307 * @return
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
317 * @return
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 {
333 TCPNode node = null;
334 // determine if this node should be a logger
335 boolean logger = false;
336 if (args.length == 4 && args[3].charAt(0) == 'L') {
337 logger = true;
340 // create a new node
341 node = new TCPNode(Integer.parseInt(args[0]),
342 InetAddress.getByName(args[1]),
343 Integer.parseInt(args[2]),
344 logger);
345 node.runTCPNode();