Skip to content

UDP Datagram Socket

opencode-agent[bot] edited this page May 10, 2026 · 1 revision

UDP Datagram Socket

JNode's bridge between java.net.DatagramSocket and the native UDP stack, providing connectionless datagram send/receive with socket options and port bindings.

Overview

UDPDatagramSocketImpl is the core socket implementation that bridges the standard java.net.DatagramSocket API to JNode's native UDP stack. It extends AbstractDatagramSocketImpl which provides the receive queue, socket options (TOS, broadcast, timeout, interface), and device binding. The class delegates to UDPProtocol for port binding, checksum computation, and packet transmission.

Two concrete implementations exist: UDPDatagramSocketImpl (direct) and PlainUDPDatagramSocketImpl (wrapping via GNU Classpath primitives). Both are created by UDPDatagramSocketImplFactory when a Java application creates a DatagramSocket.

Key Components

Class / File Role
net/src/net/org/jnode/net/ipv4/udp/UDPDatagramSocketImpl.java Primary socket implementation extending AbstractDatagramSocketImpl
net/src/net/org/jnode/net/ipv4/udp/PlainUDPDatagramSocketImpl.java GNU Classpath-compatible wrapper using inner class pattern
net/src/net/org/jnode/net/ipv4/udp/UDPDatagramSocketImplFactory.java Factory creating socket instances for the Java socket layer
net/src/net/org/jnode/net/ipv4/udp/UDPControlBlock.java Per-socket control/blocking state extending IPv4ControlBlock
net/src/net/util/AbstractDatagramSocketImpl.java Base class with receive queue, socket options, device binding
net/src/net/org/jnode/net/ipv4/udp/UDPProtocol.java Central coordinator managing port bindings and packet delivery

How It Works

Inheritance Hierarchy

java.net.DatagramSocketImpl (JDK)
    └── AbstractDatagramSocketImpl (JNode)           // receiveQueue, socket options, device
            └── UDPDatagramSocketImpl                // doBind/doClose/send/onReceive
                    └── PlainUDPDatagramSocketImpl$MyUDPDatagramSocketImpl  // inner subclass

AbstractDatagramSocketImpl provides: receive queue (Queue<SocketBuffer>), socket options (TOS, broadcast, timeout, bind address, interface), device binding, and the synchronized receive() method that dequeues from the receive queue.

Port Binding

UDPDatagramSocketImpl.doBind() delegates directly to UDPProtocol.bind(), which registers the socket in its HashMap<Integer, UDPDatagramSocketImpl> keyed by local port:

// UDPDatagramSocketImpl.java:58-60
protected void doBind(int lport, InetAddress laddr) throws SocketException {
    protocol.bind(this);
}

When DatagramSocket binds to port 0, UDPProtocol.bind() randomly selects an ephemeral port from 1024–65535 and calls socket.setLocalPort() to update the socket's port field.

Sending Datagrams

UDPDatagramSocketImpl.send() constructs the IP and UDP headers and delegates to protocol.send():

// UDPDatagramSocketImpl.java:91-108
protected void send(DatagramPacket p) throws IOException {
    final IPv4Address dstAddress = new IPv4Address(p.getAddress());
    final IPv4Header ipHdr;
    ipHdr = new IPv4Header(getTos(), getTimeToLive(), IPPROTO_UDP, dstAddress,
        p.getLength() + UDP_HLEN);
    if (!getLocalAddress().isAnyLocalAddress() || (getDevice() != null)) {
        ipHdr.setSource(new IPv4Address(getLocalAddress()));
    }
    final UDPHeader udpHdr;
    final int srcPort = getLocalPort();
    udpHdr = new UDPHeader(srcPort, p.getPort(), p.getLength());

    final SocketBuffer skbuf = new SocketBuffer(p.getData(), p.getOffset(), p.getLength());
    skbuf.setDevice(getDevice());
    protocol.send(ipHdr, udpHdr, skbuf);
}

Key behaviors:

  • Source address is set to getLocalAddress() unless it is "any local address" AND no device is bound — in that case the IP layer determines the source.
  • TTL and TOS are read from socket options via getTimeToLive() and getTos().
  • The device is attached to the SocketBuffer so the link layer knows which NIC to transmit on.

protocol.send() prefixes the UDP header (8 bytes) and passes to IPv4Service.transmit(). The UDP checksum is computed in UDPHeader.finalizeHeader() using a pseudo-header over the IP addresses.

Receiving Datagrams

When a datagram arrives, UDPProtocol.receive() parses the UDPHeader, validates the checksum, and calls deliver(). deliver() looks up the destination port in the socket map and calls socket.deliverReceived(skbuf), which queues the SocketBuffer in AbstractDatagramSocketImpl.receiveQueue:

// AbstractDatagramSocketImpl.java:268-275
public final boolean deliverReceived(SocketBuffer skbuf) {
    if (!closed) {
        receiveQueue.add(skbuf);
        return true;
    } else {
        return false;
    }
}

AbstractDatagramSocketImpl.receive() dequeues from this queue with optional timeout:

// AbstractDatagramSocketImpl.java:245-259
protected final void receive(DatagramPacket p) throws IOException {
    if (closed) throw new SocketException("DatagramSocket has been closed");
    final SocketBuffer skbuf = (SocketBuffer) receiveQueue.get(timeout);
    if (skbuf == null) {
        if (closed) throw new SocketException("DatagramSocket has been closed");
        else throw new SocketTimeoutException("Timeout in receive");
    } else {
        onReceive(p, skbuf);
    }
}

UDPDatagramSocketImpl.onReceive() extracts the data and addressing from the SocketBuffer:

// UDPDatagramSocketImpl.java:80-86
protected void onReceive(DatagramPacket p, SocketBuffer skbuf) throws IOException {
    final IPv4Header ipHdr = (IPv4Header) skbuf.getNetworkLayerHeader();
    final UDPHeader udpHdr = (UDPHeader) skbuf.getTransportLayerHeader();
    p.setData(skbuf.toByteArray(), 0, skbuf.getSize());
    p.setAddress(ipHdr.getSource().toInetAddress());
    p.setPort(udpHdr.getSrcPort());
}

Socket Factory Registration

During UDPProtocol construction, the factory is registered with the JDK:

// UDPProtocol.java:100-108
dsiFactory = new UDPDatagramSocketImplFactory(this);
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
    public Object run() throws IOException {
        DatagramSocket.setDatagramSocketImplFactory(dsiFactory);
        PlainDatagramSocketImpl.setUDPFactory(dsiFactory);
        return null;
    }
});

This ensures all DatagramSocket instances in JNode use JNode's UDP implementation instead of a host-JDK fallback.

Socket Options

AbstractDatagramSocketImpl provides:

Option Type Behavior
IP_TOS int Type of Service field in outgoing IP header
SO_BROADCAST boolean Allow sending to broadcast addresses (default: true)
SO_BINDADDR InetAddress Local bind address (read-only)
SO_TIMEOUT int Receive timeout in milliseconds (0 = infinite)
SO_TRANSMIT_IF NetworkInterface Outgoing network interface/device
SO_RCVBUF int Returns ETH_FRAME_LEN (stub)
SO_SNDBUF int Returns ETH_FRAME_LEN (stub)

TTL is managed separately via getTimeToLive()/setTimeToLive().

Closing

UDPDatagramSocketImpl.doClose() calls protocol.unbind() which removes the socket from the port map. AbstractDatagramSocketImpl.close() sets closed = true and closes the receive queue.

Gotchas

  • Connectionless by design: Unlike TCPSocketImpl, UDPDatagramSocketImpl has no connection state. There is no connect() call that creates session state — connect() on a UDP socket only sets the default destination.
  • Data copy on receive: onReceive() calls skbuf.toByteArray() which copies the payload into a new byte array for the DatagramPacket. This is unavoidable since DatagramPacket.data is a fixed byte[].
  • TTL/TOS stubs: getTTL() and setTTL(byte) in UDPDatagramSocketImpl are empty stubs — they read/write the ttl field (from AbstractDatagramSocketImpl) but send() reads via getTimeToLive() which returns the int field.
  • No multicast support: join()/leave()/joinGroup()/leaveGroup() are all stubs in AbstractDatagramSocketImpl. Multicast is not implemented.
  • PlainUDPDatagramSocketImpl debug output: Contains System.out.println calls for "udp:" and "packet:" which should be removed or replaced with proper logging.
  • Source port comment: A TODO in send() notes uncertainty about whether srcPort should come from the socket's local port or the packet's port — the current code uses getLocalPort().
  • Receive queue is unbounded: The Queue<SocketBuffer> has no capacity limit. A flood of incoming datagrams could exhaust memory. Applications expecting high traffic should use setOption(SO_TIMEOUT, ...) to prevent unbounded blocking on receive().

Related Pages

  • UDP-Protocol — The underlying UDP transport protocol, header format, checksum calculation, port management
  • Sockets-Implementation — Hub page bridging java.net API to the native network stack
  • Network-Stack — Hub page for JNode's complete network architecture
  • Network-Layers — SocketBuffer pipeline, protocol layering model

Clone this wiki locally