views:

305

answers:

5

Hi

I am doing my assignment in Network architecture 1, where I have to implement a distance vector routing at each node.

At each node, I have a thread which listens for incoming DatagramPackets containing routing information from neighboring nodes only on a specific port. When a datagram arrives, the thread processes that datagram, and if there are updates in its internal routing tables, then it sends its routing information to all of its neighbors.

I am trying to do it in Java.

The problem I am facing is that when a datagram arrives, I need to process it. If during that time any other datagram arrives, it is dropped, as the thread is currently processing information. That means I have a loss of information.

Can any one help me with this?

I am using the usual way of reading from a socket in java.

DatagramSocket socket = new DatagramSocket(4445, InetAddress.getByName("127.0.0.1"));
while (true) {
    try {
        byte[] buf = new byte[2000];

        // receive request
        DatagramPacket recvRequest = new DatagramPacket(buf, buf.length);

        socket.receive(recvRequest);

        //Some process of data in datagram

    } catch (IOException e) {
        e.printStackTrace();
    }
}
+2  A: 

You can process the received datagram in a thread, so your thread with the socket listener can continue to receive new datagrams.

Mr. Will
A: 

I haven't done this in Java, but, you can (or should) pass more than one simultaneous datagram buffer to the socket (either with several threads each invoking the synchrnonous receive method, or preferably with one thread invoking an asynchrnonous receive method more than once).

The advantage of passing multiple simultaneous datagram buffers to the socket is obvious: i.e. the socket will still have a buffer (into which to receive the next datagram) even while it has already filled one buffer (with a previous datagram) and passed that buffer back to you.

You might ask, "in what sequence will the buffers be passed back to me?" and the answer to that is, "it shouldn't matter." If the sequence in which you process datagrams is important then the datagrams themselves should contain a sequence number (because datagrams might get out of sequence as they're routed over the network, whether or not you've passed multiple simultaneous to the local socket with a consequent possibility of "simultaneous" receives being delivered back to you out of sequence).

ChrisW
A: 

DatagramSocket socket = new DatagramSocket(4445, InetAddress.getByName("127.0.0.1")); while (true) { try { // note final .. final byte[] buf = new byte[2000];

    // receive request
    DatagramPacket recvRequest = new DatagramPacket(buf, buf.length);

    socket.receive(recvRequest);

    //Some process of data in datagram
    (new Thread(new Runnable() {
        public void run () {
            // do stuff with data in buf
            ...
        }
    })).start();

} catch (IOException e) {
    e.printStackTrace();
}

}

Performance note:Its certainly not efficient to instantiate a new Thread for every packet. If processing order is of importance, use a BlockingQueue<byte[]> and have a pre-instantiated processing thread read off buffers in a FIFO manner. If order is irrelevant, use an Executor.(And if you really want to squeeze the cycles, consider recycling those byte[] buffers ..)
A: 

It is worth remembering UDP is lossy transport, while minimising packet loss is a good idea you should never assume you will get every packet, (or that the packets will arrive in the order you sent them)

Peter Lawrey
+1  A: 

Hi

This the final project that i submitted. It might be having some improper documentation and some bad usage of Java. As this project runs on local system, instead of using different IP address and same port number, i am doing it other way.

NetworkBoot.java provides the initial neighbor details to each router.

Thanks -Sunny Jain

enter code here

/* * File Name : Router.java * Public Class Name : Router * */

//~--- JDK imports ------------------------------------------------------------

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetAddress;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Set;

import java.util.concurrent.LinkedBlockingQueue;

import javax.swing.SwingUtilities;

/** * * NA1 project 2 spring 2009 semester * @author sunny jain * * */

public class Router extends Thread {

/**
 * HashMap containing list of neighbors and cost to reach them.
 */
private HashMap<Integer, Integer> hmapDirectNeighbours = new HashMap<Integer, Integer>(61);
/**
 * HashMap containing list of destination as key and routing info to them as value.
 * Routing info contains RouteDetail object.
 * @see RouteDetail
 */
private HashMap<Integer, RouteDetail> hmapRoutes = new HashMap<Integer, RouteDetail>();
/**
 * DatagramSocket
 */
private DatagramSocket dSoc;
/**
 * DatagramPacket
 */
private DatagramPacket dpackReceive,  dpackSend;
/**
 * Inetaddress of system on which runs this algorithm.
 */
private InetAddress localAddress;
/**
 * port to listen at for incoming route info from neighbors.
 */
int port;
private LinkedBlockingQueue<DatagramPacket> lbq = new LinkedBlockingQueue<DatagramPacket>();

/**
 * Made constructor private to force initialization by specifying port
 * compulsory.
 */
private Router() {
}

/**
 * Constuctor taking port number as parameter and creates a datagramSocket
 * to listen for incoming DatagramPacket on that socket.
 * @param port
 */
public Router(int port) {
    try {
        this.port = port;
        localAddress = InetAddress.getByName("127.0.0.1");
        dSoc = new DatagramSocket(port, localAddress);
    } catch (Exception ex) {
        System.out.println("Error while creating socket : " + ex.getMessage());
    }
    this.start();

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            while (true) {
                try {
                    received_Route_Info(lbq.take());
                } catch (InterruptedException ex) {
                    System.out.println("Error while reading elements from datagram queue");
                }}}});
}

public void setRouterBootInfo(String strNeighboursInfo) {
    String[] strNeighbouringNodes = strNeighboursInfo.split(";");

   for (int i = 0; i < strNeighbouringNodes.length; i++) {

        String[] strNodeIpAndPort = strNeighbouringNodes[i].split(":");

        hmapDirectNeighbours.put(Integer.valueOf(strNodeIpAndPort[0]), Integer.valueOf(strNodeIpAndPort[1]));
        hmapRoutes.put(Integer.valueOf(strNodeIpAndPort[0]), new RouteDetail(null, Integer.valueOf(strNodeIpAndPort[1])));
    }
    propagateChanges();
// entry in Route table....No need for infinity as we creat entry when a node is reachable.
}

@Override
public void run() {
    while (true) {
        try {
            byte[] buf = new byte[250];
            // receive request
            dpackReceive = new DatagramPacket(buf, buf.length);
            dSoc.receive(dpackReceive);
            lbq.put(dpackReceive);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
            dSoc.close();
        } catch (IOException e) {
            e.printStackTrace();
            dSoc.close();
        }
    }


}

/**
 * This method is called for each DatagramPacket received containing new
 * routing information.
 *
 * This method checks whether this packet came from neighboring node
 * (routers) only. If true it applies Distance vector algorithm on data
 * present in datagram packet and due to this information if their is any
 * change in local routing information that it displays current local
 * updated routing information and also sends this updated information to
 * other neighbours only.
 *
 * @param dataPckt
 * @see  #validate_Is_Packet_From_Neighbor(java.net.DatagramPacket)
 * @see #apply_Routing_Algorithm(java.net.DatagramPacket, java.util.HashMap)
 * @see #print_route_info()
 * @see #send_Updates_To_Neighbors(routesInfo)
 */
private void received_Route_Info(DatagramPacket dataPckt) {
    if (dataPckt.getPort() == 4000) {
        setRouterBootInfo(getStringFromBytes(dataPckt));
    } else if (validate_Is_Packet_From_Neighbor(dataPckt)) {
        if (apply_Routing_Algorithm(dataPckt, create_HashMap_Routes(getStringFromBytes(dataPckt)))) {

            // if their is change in routing information.
            propagateChanges();
        }
    }
}

/**
 * Validates whether the Datagram packet received is from the neighbors only.
 * @param datagrampckt DatagramPacket comtaining routing information.
 * @return true if datagrampckt is from neighbors only otherwise false.
 */
private boolean validate_Is_Packet_From_Neighbor(DatagramPacket datagrampckt) {
    return hmapDirectNeighbours.containsKey(Integer.valueOf(datagrampckt.getPort()));
}

/**
 * Returns byte representaion of data contained in DatagramPacket pkt.
 * @param pkt DatagramPacket
 * @return byte representation of data contained in pkt
 */
private String getStringFromBytes(DatagramPacket pkt) {
    String strData = new String(pkt.getData());
    return strData.substring(0, strData.lastIndexOf(';'));
}

/**
 * Applies Distance Vector algorithm using newly received routing information
 * and information presently with this node (Router).
 * @param datagrampckt DatagramPacket containing routing information.
 * @param newRoutes HashMap of routes new information received with
 * destination as key and cost to that destination as value.
 */
private boolean apply_Routing_Algorithm(DatagramPacket dataPckt, HashMap<Integer, Integer> newRoutes) {
    boolean updated = false;
    Integer pktSourse = Integer.valueOf(dataPckt.getPort());

    // Get a set of the routes
    Set<Integer> set = newRoutes.keySet();

    // Get an iterator
    Iterator<Integer> iterator = set.iterator();

    // Display elements.
    while (iterator.hasNext()) {
        Integer key = iterator.next();
        Integer nextHopCost = hmapRoutes.get(pktSourse).getPathCost();
        int optionalCost = newRoutes.get(key) + (nextHopCost == null ? 0 : nextHopCost);
        if (hmapRoutes.containsKey(key)) {
            RouteDetail routeDetail = hmapRoutes.get(key);

            if (routeDetail.getPathCost().compareTo(optionalCost) > 0) {
                routeDetail.setNextHop(pktSourse);
                routeDetail.setPathCost(optionalCost);
                hmapRoutes.put(key, routeDetail);
                updated = true;

            // try to verify above statement
            }
        } else {
            if (!key.equals(port)) {
                RouteDetail newRouteDetail = new RouteDetail(pktSourse, optionalCost);
                hmapRoutes.put(key, newRouteDetail);
                updated = true;
            }
        }
    }

    return updated;
}

/**
 * When internal routing information is chaged, send this information to
 * other neighbors.
 * @param routesInfo byte representaion of routing information.
 */
private void send_Updates_To_Neighbors(byte[] routesInfo) {

    // Get a set of the routes
    Set<Integer> set = hmapDirectNeighbours.keySet();

    // Get an iterator
    Iterator<Integer> iterator = set.iterator();

    // Display elements.
    while (iterator.hasNext()) {
        dpackSend = new DatagramPacket(routesInfo, routesInfo.length, localAddress, iterator.next().intValue());

        try {
            dSoc.send(dpackSend);
        } catch (IOException ex) {
            System.out.println("Error while sending route updates : " + ex.getMessage());
        }
    }
}

/**
 * Parses routeInfo to creat an HashMap based on this informationin the
 * format as HashMap of <<Integer:Destination>,<Integer: Cost to this destination>>
 * @param routeInfo contains routing information as String in the syntax
 * of {<Destination>:<Cost to destination>;}
 * @return Hashmap<<Integer:Destination>,<Integer: Cost to this destination>>
 */
private HashMap<Integer, Integer> create_HashMap_Routes(String routeInfo) {
    HashMap<Integer, Integer> routes = new HashMap<Integer, Integer>();
    String[] straRoute = routeInfo.split(";");

    for (int i = 0; i < straRoute.length; i++) {
        String[] straDestAndCost = straRoute[i].split(":");

        routes.put(Integer.parseInt(straDestAndCost[0]), Integer.parseInt(straDestAndCost[1]));
    }

    return routes;
}

/**
 * Converts current routing information stored as HashMap to String
 * presentation in format as {<Destination>:<Cost to destination>;}
 *
 * @return String representaion of routing information.
 * @see #hmapRoutes.
 */
private String create_String_Of_Routes() {
    StringBuilder strB = new StringBuilder();

    // Get a set of the routes
    Set<Integer> set = hmapRoutes.keySet();

    // Get an iterator
    Iterator<Integer> iterator = set.iterator();

    // Display elements.
    while (iterator.hasNext()) {
        Integer destination = iterator.next();

        strB.append(destination);
        strB.append(":");
        strB.append(hmapRoutes.get(destination).getPathCost());
        strB.append(";");
    }

    return strB.toString();
}

/**
 * Prints the current routing information stored in <code>hmapRoutes</code>
 * to default output stream of this program.
 * @see #hmapRoutes.
 */
public void print_route_info() {
    RouteDetail route;
    StringBuilder builder;

    // PRINT THE CURRENT ROUTING INFO AT THIS NODE
    System.out.println("");
    System.out.println("    TABLE AT NODE WITH PORT  : " + port);
    System.out.println("--------------------------------------------------------------------------------");
    System.out.println("\t\tTo  \t|\t Via\t|\tCost\t\t");
    System.out.println("--------------------------------------------------------------------------------");

    // Get a set of the routes
    Set<Integer> set = hmapRoutes.keySet();

    // Get an iterator
    Iterator<Integer> iterator = set.iterator();

    // Display elements.
    while (iterator.hasNext()) {
        Integer key = iterator.next();

        route = hmapRoutes.get(key);
        builder = new StringBuilder();
        builder.append("\t\t" + key.intValue());
        builder.append("\t|\t" + (route.getNextHop() == null ? "   -" : route.getNextHop()));
        builder.append("\t|\t" + route.getPathCost() + "\t\t");
        System.out.println(builder.toString());
    }
}

/**
 * This class provides details for each destination.
 * It provides detail of cost that will be incurred to reach that
 * destination and next router on that path.
 */

private class RouteDetail {

    Integer nextHop;
    Integer pathCost;

    public RouteDetail(Integer nextHop, Integer pathCost) {
        this.nextHop = nextHop;
        this.pathCost = pathCost;
    }

    public Integer getNextHop() {
        return nextHop;
    }

    public void setNextHop(Integer nextHop) {
        this.nextHop = nextHop;
    }

    public Integer getPathCost() {
        return pathCost;
    }

    public void setPathCost(Integer pathCost) {
        this.pathCost = pathCost;
    }
}

private void propagateChanges() {
    print_route_info();
    send_Updates_To_Neighbors(create_String_Of_Routes().getBytes());
}

public static void main(String[] args) {
    new Router(Integer.parseInt(args[0]));
}

}

/* * File Name : NetworkBoot.java * Public Class Name : NetworkBoot * */

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetAddress;

/** * * NA1 project 2 spring 2009 semester * @author sunny jain * * */

public class NetworkBoot {

public static void main(String[] args) {
    try {
        DatagramSocket dSoc = new DatagramSocket(4000, InetAddress.getByName("127.0.0.1"));
        String[] sendD = {"4006:3;4007:5;4009:2;", "4005:3;4007:3;4008:6;", "4005:5;4006:3;", "4009:2;4006:6;", "4008:2;4005:2;"};
        for (int i = 0, port = 4005; i < 5; i++) {
            dSoc.send(new DatagramPacket(sendD[i].getBytes(), sendD[i].length(), InetAddress.getByName("127.0.0.1"), port++));
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

}