4. Unet basics

Now that we have a basic understanding of how UnetStack works, it is time to take the next step into setting up and configuring underwater networks, or simply Unets .

4.1. Node names and addresses

Unet nodes are identified by unique addresses within the Unet. Small Unets might use 8-bit addresses, supporting up to 255 different nodes. Larger Unets might use 16-bit addresses, supporting up to 65535 different nodes in the network. The address space is controlled by the parameter node.addressSize , and must be set to the same value (either 8 or 16) on all nodes in a Unet.

The code examples in this chapter assume that you have a simulated Unet (the 2-node-network simulation from Chapter 2 ) running, and you’re connected to the shell of one of the nodes. However, if you have access to modems, you may choose to use the real Unet and connect to the shell of one of the modem nodes.

To check the current address size on your node:

> node
<<< NodeInfo >>>

[org.arl.unet.nodeinfo.NodeInfoParam]
  nodeName = A
  address = 232
  addressSize = 8
  canForward = true
  mobility = false
  location = [0.0, 0.0, -15.0]
  origin = [NaN, NaN]

Some node parameters have not been shown in the above listing for brevity.

Address 0 is a broadcast address. All other addresses may be assigned to nodes in a Unet. Each Unet node is also associated with a node name ( node.nodeName ). If a node name is not explicity set, it defaults to the string representation of the node address. Descriptive node names may be used, if desired:

> node.nodeName = 'buoy_A'
buoy_A
> node
<<< NodeInfo >>>

[org.arl.unet.nodeinfo.NodeInfoParam]
  nodeName = buoy_A
  address = 232
  addressSize = 8
  canForward = true
  mobility = false
  location = [1000.0, 0.0, -15.0]
  origin = []

It is recommended that, if descriptive node names are used, the corresponding node addresses be set using the ADDRESS_RESOLUTION service. This ensures that name-to-address resolution leads to the correct address for the node. The ADDRESS_RESOLUTION service can be accessed via the host() shell command:

> host('buoy_A')
68
> node.address = host(node.nodeName)
68

The default ADDRESS_RESOLUTION agent in the UnetStack maps node names to node addresses using a hash function. The method reduces network traffic for host name resolution, but can lead to address conflicts between nodes if two names happen to map to the same address. It is the responsibility of the network engineer to resolve address conflicts manually during the setup of the network, if the default ADDRESS_RESOLUTION agent is used. For small networks, this is simply a matter of checking that all chosen node names in the network lead to unique node addresses:

> ['buoy_A', 'auv_1', 'auv_2', 'sensor_adcp1', 'sensor_ctd1'].each { name ->
-   print "${name}: ${host(name)}"
- };
buoy_A: 68
auv_1: 150
auv_2: 109
sensor_adcp1: 43
sensor_ctd1: 14

4.2. Protocol numbers

Datagrams represent packets of data sent between nodes. Each node may have multiple agents and applications running on it, and so we need a way to specify which application the datagram is meant for. To aid with this, each datagram is associated with a protocol number that identifies the consumer on the destination node that the datagram is intended for. The consumer may be an agent or an end-user application. Protocol numbers can be thought of as port numbers in TCP/IP or UDP/IP.

The consumer may be an agent or an end-user application. Protocol number 0 ( Protocol.DATA ) is used for generic application data. Protocol numbers from 1 to Protocol.USER-1 (31) are reserved for use by default stack agents. Protocol numbers from Protocol.USER (32) to Protocol.MAX (63) are available for end-user applications to use.

On node B, type:

> s = new UnetSocket(this);
> s.bind(Protocol.USER)                 // listen for datagrams with Protocol.USER
> rx = s.receive()

to wait for a reception with Protocol.USER .

On node A, type:

> s = new UnetSocket(this);
> s.connect(host('B'), Protocol.DATA)  // send datagrams with Protocol.DATA
> s.send('hi!' as byte[])
> s.connect(host('B'), Protocol.USER)  // send datagrams with Protocol.USER
> s.send('hello!' as byte[])
true
> s.close()

Node B will receive only the second message, since it is listening for datagrams with Protocol.USER only. We can confirm this by checking the data in the received datagram on the command shell for node B, and close the socket:

DatagramNtf:INFORM[from:68 to:31 protocol:32 (6 bytes)]
> new String(rx.data)
hello!
> s.close()
<<< [UnetStack basics] [Setting up small networks] >>>