1 /* Copyright (c) 2012-2018. The SimGrid Team.
2 * All rights reserved. */
4 /* This program is free software; you can redistribute it and/or modify it
5 * under the terms of the license (GNU LGPL) which comes with this package. */
9 import org.simgrid.msg.Host;
11 import org.simgrid.msg.Msg;
12 import org.simgrid.msg.Comm;
13 import org.simgrid.msg.Task;
14 import org.simgrid.msg.Process;
15 import org.simgrid.msg.MsgException;
17 public class Node extends Process {
19 protected RoutingTable table;
20 protected int deadline;
21 protected int findNodeSuccedded = 0;
22 protected int findNodeFailed = 0;
25 public Node(Host host, String name, String[]args) {
26 super(host,name,args);
30 public void main(String[] args) throws MsgException {
31 //Check the number of arguments.
32 if (args.length != 2 && args.length != 3) {
33 Msg.info("Wrong argument count.");
36 this.id = Integer.parseInt(args[0]);
37 this.table = new RoutingTable(this.id);
39 if (args.length == 3) {
40 this.deadline = Integer.parseInt(args[2]);
41 Msg.info("Hi, I'm going to join the network with the id " + id + "!");
42 if (joinNetwork(Integer.parseInt(args[1]))) {
46 Msg.info("I couldn't join the network :(");
50 this.deadline = Integer.parseInt(args[1]);
51 Msg.info("Hi, I'm going to create the network with the id " + id + "!");
52 table.update(this.id);
55 Msg.debug("I'm leaving the network");
56 Msg.debug("Here is my routing table:" + table);
59 public void mainLoop() {
60 double nextLookupTime = Msg.getClock() + Common.RANDOM_LOOKUP_INTERVAL;
61 while (Msg.getClock() < this.deadline) {
64 comm = Task.irecv(Integer.toString(id));
67 if (Msg.getClock() >= nextLookupTime) {
69 nextLookupTime += Common.RANDOM_LOOKUP_INTERVAL;
74 Task task = comm.getTask();
80 Msg.debug("Caught exception: " + e);
83 Msg.info(findNodeSuccedded + "/" + (findNodeSuccedded + findNodeFailed) + " FIND_NODE have succedded.");
87 * @brief Try to make the node join the network
88 * @param idKnown Id of someone we know in the system
90 public boolean joinNetwork(int idKnown) {
91 boolean answerGot = false;
92 double timeBegin = Msg.getClock();
93 Msg.debug("Joining the network knowing " + idKnown);
94 //Add ourselves and the node we know to our routing table
95 table.update(this.id);
96 table.update(idKnown);
97 //Send a "FIND_NODE" to the node we know.
98 sendFindNode(idKnown,this.id);
99 //Wait for the answer.
105 comm = Task.irecv(Integer.toString(id));
111 Task task = comm.getTask();
112 if (task instanceof FindNodeAnswerTask) {
113 //Retrieve the node list and ping them
114 FindNodeAnswerTask answerTask = (FindNodeAnswerTask)task;
115 Answer answer = answerTask.getAnswer();
117 if (answer.getDestinationId() == this.id) {
118 //Ping everyone in the list
119 for (Contact c : answer.getNodes()) {
120 table.update(c.getId());
130 catch (Exception ex) {
132 Msg.info("FIND_NODE failed");
134 } while (!answerGot && trials < Common.MAX_JOIN_TRIALS);
135 /* Second step: Send a FIND_NODE in a node in each bucket */
136 int bucketId = table.findBucket(idKnown).getId();
137 for (int i = 0; ((bucketId - i) > 0 ||
138 (bucketId + i) <= Common.IDENTIFIER_SIZE) &&
139 i < Common.JOIN_BUCKETS_QUERIES; i++) {
140 if (bucketId - i > 0) {
141 int idInBucket = table.getIdInPrefix(this.id,bucketId - i);
142 this.findNode(idInBucket,false);
144 if (bucketId + i <= Common.IDENTIFIER_SIZE) {
145 int idInBucket = table.getIdInPrefix(this.id,bucketId + i);
146 findNode(idInBucket,false);
149 Msg.debug("Time spent:" + (Msg.getClock() - timeBegin));
153 /* Send a request to find a node in the node's routing table. */
154 public boolean findNode(int destination, boolean counts) {
158 boolean destinationFound;
160 double timeBeginReceive;
162 double globalTimeout = Msg.getClock() + Common.FIND_NODE_GLOBAL_TIMEOUT;
163 //Build a list of the closest nodes we already know.
164 Answer nodeList = table.findClosest(destination);
165 Msg.verb("Doing a FIND_NODE on " + destination);
167 timeBeginReceive = Msg.getClock();
169 queries = this.sendFindNodeToBest(nodeList);
171 timeout = Msg.getClock() + Common.FIND_NODE_TIMEOUT;
176 comm = Task.irecv(Integer.toString(id));
181 Task task = comm.getTask();
182 if (task instanceof FindNodeAnswerTask) {
183 FindNodeAnswerTask answerTask = (FindNodeAnswerTask)task;
184 //Check if we received what we are looking for.
185 if (answerTask.getDestinationId() == destination) {
186 table.update(answerTask.getSenderId());
187 //Add the answer to our routing table
188 for (Contact c: answerTask.getAnswer().getNodes()) {
189 table.update(c.getId());
193 nodesAdded = nodeList.merge(answerTask.getAnswer());
195 /* If it's not our answer, we answer to the node that has queried us anyway */
197 //Update the timeout if it's not our answer.
198 timeout += Msg.getClock() - timeBeginReceive;
199 timeBeginReceive = Msg.getClock();
203 timeout += Msg.getClock() - timeBeginReceive;
204 timeBeginReceive = Msg.getClock();
209 catch (Exception e) {
212 } while (answers < queries && Msg.getClock() < timeout);
213 destinationFound = nodeList.destinationFound();
214 } while (!destinationFound && (nodesAdded > 0 || answers == 0) && Msg.getClock() < globalTimeout
215 && steps < Common.MAX_STEPS);
217 if (destinationFound) {
221 Msg.debug("Find node on " + destination + " succedded");
223 Msg.debug("Find node on " + destination + " failed");
224 Msg.debug("Queried " + queries + " nodes to find " + destination);
225 Msg.debug(nodeList.toString());
230 return destinationFound;
234 * @brief Sends a "PING" request to a node
235 * @param destination Ping destination id.
237 public void ping(int destination) {
238 boolean destinationFound = false;
239 double timeout = Msg.getClock() + Common.PING_TIMEOUT;
240 PingTask pingTask = new PingTask(this.id);
241 /* Sending the ping task */
242 pingTask.dsend(Integer.toString(destination));
245 Task task = Task.receive(Integer.toString(this.id),Common.PING_TIMEOUT);
246 if (task instanceof PingAnswerTask) {
247 PingAnswerTask answerTask = (PingAnswerTask)task;
248 if (answerTask.getSenderId() == destination) {
249 this.table.update(destination);
250 destinationFound = true;
259 catch (Exception ex) {
260 Msg.debug("Caught exception: " + ex);
262 } while (Msg.getClock() < timeout && !destinationFound);
266 * @brief Sends a "FIND_NODE" request (task) to a node we know.
267 * @param id Id of the node we are querying
268 * @param destination id of the node we are trying to find.
270 public void sendFindNode(int id, int destination) {
271 Msg.debug("Sending a FIND_NODE to " + Integer.toString(id) + " to find " + destination );
272 FindNodeTask task = new FindNodeTask(this.id,destination);
273 task.dsend(Integer.toString(id));
276 /** Sends a "FIND_NODE" request to the best "alpha" nodes in a node list */
277 public int sendFindNodeToBest(Answer nodeList) {
278 int destination = nodeList.getDestinationId();
280 for (i = 0; i < Common.ALPHA && i < nodeList.size(); i++) {
281 Contact node = nodeList.getNodes().get(i);
282 if (node.getId() != this.id) {
283 this.sendFindNode(node.getId(),destination);
289 public void randomLookup() {
294 * @brief Handles an incomming task
295 * @param task The task we need to handle
297 public void handleTask(Task task) {
298 if (task instanceof KademliaTask) {
299 table.update(((KademliaTask) task).getSenderId());
300 if (task instanceof FindNodeTask) {
301 handleFindNode((FindNodeTask)task);
303 else if (task instanceof PingTask) {
304 handlePing((PingTask)task);
309 public void handleFindNode(FindNodeTask task) {
310 Msg.debug("Received a FIND_NODE from " + task.getSenderId());
311 Answer answer = table.findClosest(task.getDestination());
312 FindNodeAnswerTask taskToSend = new FindNodeAnswerTask(this.id,task.getDestination(),answer);
313 taskToSend.dsend(Integer.toString(task.getSenderId()));
316 public void handlePing(PingTask task) {
317 Msg.debug("Received a PING from " + task.getSenderId());
318 PingAnswerTask taskToSend = new PingAnswerTask(this.id);
319 taskToSend.dsend(Integer.toString(task.getSenderId()));