UDP Protocol
The NetBurner API provides two methods to implement UDP: a C++ class interface and traditional socket functions. Both methods offer equivalent performance; choose based on your coding preference.
UDP Implementation Comparison
Class Interface Socket Interface
┌──────────────┐ ┌──────────────┐
└──────────────┘ └──────────────┘
Object-oriented Function-based
UDP Packet Class - Complete UDP packet management.
Definition udp.h:602
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, unsigned long timeout)
Wait for events to occur on one or more I/O resources associated with a set of file descriptors (fds)...
int recvfrom(int sock, puint8_t buffer, int len, IPADDR *pAddr, uint16_t *pLocal_port, uint16_t *pRemote_port)
Receive UDP packet from socket with sender information.
Definition udp.h:5960
int sendto(int sock, puint8_t what_to_send, int len_to_send, const IPADDR &to_addr, uint16_t remote_port)
Send UDP packet through socket to specified destination.
Definition udp.h:5876
UDP Class Interface
The UDPPacket class provides an object-oriented approach to UDP communication.
Receiving UDP Packets (Class Method)
Setup Process**:
- Create an OS_FIFO to hold incoming packets
- Register the FIFO with
RegisterUDPFifo()
for specific port(s)
- Construct UDPPacket object to receive packets (blocking)
- Validate packet with
Validate()
member function
Extract data with GetDataBuffer()
member function
Multi-Port Listening**:
- Use separate OS_FIFO for each port, OR
- Use single OS_FIFO for multiple ports by calling
RegisterUDPFifo()
multiple times
Sending UDP Packets (Class Method)
Transmission Process**:
- Declare UDPPacket instance
- Set source and destination ports
- Add data using member functions
- Call
Send()
to transmit packet
UDP Class Example
This example demonstrates bidirectional UDP communication using the UDPPacket class.
#include <init.h>
#include <stdlib.h>
#include <system.h>
#include <udp.h>
#include <utils.h>
const char *AppName = "UDP Send/Receive Example";
uint32_t UdpReceiveTaskStack[USER_TASK_STK_SIZE];
void UdpReceiveTask(void *pd)
{
static OS_FIFO fifo;
int listenPort = (int)pd;
printf("Listening on UDP port: %d\r\n", listenPort);
while (1)
{
if (upkt.Validate())
{
uint16_t len = upkt.GetDataSize();
printf("Received UDP packet with %d bytes from: %I\r\n",
(int)len, upkt.GetSourceAddress());
printf("\r\n");
}
}
}
void UserMain(void *pd)
{
int portNumber;
char buffer[80];
printf("Application: %s\r\nNNDK Revision: %s\r\n",
printf("Enter the UDP port number (send & receive): ");
gets(buffer);
portNumber = atoi(buffer);
printf("\r\nEnter the destination IP Address: ");
gets(buffer);
destIpAddress = AsciiToIp(buffer);
printf("Listening/Sending on UDP Port %d, Sending to: %I\r\n",
portNumber, destIpAddress);
OSTaskCreatewName(UdpReceiveTask,
(void *)portNumber,
&UdpReceiveTaskStack[USER_TASK_STK_SIZE],
UdpReceiveTaskStack,
"UDP Receive");
printf("Enter data and hit return to send.\r\n");
while (1)
{
gets(buffer);
printf("\r\n");
printf("Sent \"%s\" to %I:%d\r\n", buffer, destIpAddress, portNumber);
}
}
Used to hold and manipulate IPv4 and IPv6 addresses in dual stack mode.
Definition ipv6_addr.h:41
void Send(const IPADDR &to, uint8_t ttl=0)
Send and free buffer (recommended - most efficient)
Definition udp.h:3391
void SetDestinationPort(uint16_t port)
Set destination port (standard services: 53=DNS, 80=HTTP, 123=NTP, 161=SNMP)
void SetSourcePort(uint16_t port)
Set source port (0=auto-assign ephemeral port, 1024-65535=specific port)
void AddData(puint8_t pData, uint16_t len)
Add binary data (auto-updates size - no SetDataSize() needed)
void AddDataByte(uint8_t b)
Add 8-bit value.
void ShowData(const uint8_t *fromptr, uint16_t len)
Dump a block of data to stdio and show in ASCII and hex.
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130
bool RegisterUDPFifo(uint16_t listenPort, OS_FIFO *pFifo)
Register FIFO to receive UDP packets on specified port.
const char * GetReleaseTag()
Returns the NNDK release tag information.
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
UDP Class Architecture
Task Architecture:
┌────────────────────────────────────────────────┐
│ UserMain Task │
│ ┌───────────────────────────────────────────┐ │
│ │ 1. Get port and destination IP │ │
│ │ 2. Create UdpReceiveTask │ │
│ │ 3. Loop: │ │
│ │ ├─> Read user input │ │
│ │ ├─> Set ports │ │
│ │ ├─> Add data │ │
│ │ └─> Send() │ │
│ └───────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ UdpReceiveTask (Higher Priority) │
│ ┌───────────────────────────────────────────┐ │
│ │ 1. Create OS_FIFO │ │
│ │ 3. Loop: │ │
│ │ │ [BLOCKS until packet received] │ │
│ │ ├─> Validate() │ │
│ │ ├─> GetDataBuffer() │ │
│ │ └─> Display data │ │
│ └───────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘
Key Class Methods
Sending**:
Receiving**:
OS_FIFO fifo;
if (upkt.Validate()) {
uint8_t *data = upkt.GetDataBuffer();
uint16_t len = upkt.GetDataSize();
IPADDR source = upkt.GetSourceAddress();
}
UDP Sockets Interface
The socket-based UDP interface provides a traditional function-based approach to UDP communication.
Receiving UDP Packets (Socket Method)
Setup Process**:
- Call
CreateRxUdpSocket()
to open listening socket (returns file descriptor)
- Call
recvfrom()
to receive packets (blocks by default)
- Optionally wrap
recvfrom()
in select()
for timeout control
Sending UDP Packets (Socket Method)
Transmission Process**:
- Call
CreateTxUdpSocket()
to open socket (returns file descriptor)
- Call
sendto()
to transmit packets
UDP Socket Error Processing
These negative values are returned by UDP socket creation and operation functions to indicate specific error conditions. All successful operations return non-negative values (valid file descriptors >= 0 or byte counts >= 0).
#define UDP_ERR_NOSUCH_SOCKET (-1)
#define UDP_ERR_NOTOPEN_TO_WRITE (-2)
#define UDP_ERR_NOTOPEN_TO_READ (-3)
UDP_ERR_NOSUCH_SOCKET
Socket does not exist or operation failed
#define UDP_ERR_NOSUCH_SOCKET (-1)
Returned when:
- Socket creation fails (port conflict, resource exhaustion)
- Invalid socket descriptor used in operation
- Socket was closed but still being referenced
- System cannot allocate required resources
Common Scenarios:
Scenario 1: Port Already in Use
int CreateRxUdpSocket(uint16_t listening_port)
Create receive-only UDP socket bound to specified port.
Scenario 2: Using Closed Socket
int result =
sendto(sock, data, len, ip, port);
int close(int fd)
Close the specified file descriptor and free the associated resources.
Scenario 3: Resource Exhaustion
for (int i = 0; i < 100; i++) {
if (sock < 0) {
printf("Failed at socket %d: UDP_ERR_NOSUCH_SOCKET\r\n", i);
break;
}
}
UDP_ERR_NOTOPEN_TO_WRITE
Socket not configured for sending operations
#define UDP_ERR_NOTOPEN_TO_WRITE
Socket not open for write.
Definition udp.h:4763
Returned when attempting to send data through a socket that was created in receive-only mode (using CreateRxUdpSocket()). The socket can only receive packets, not send them.
Common Scenarios:
Scenario 1: Sending on Receive-Only Socket
uint8_t data[] = "Hello";
int result =
sendto(sock, data,
sizeof(data), destIP, 8080);
int result =
sendto(sock, data,
sizeof(data), destIP, 8080);
int CreateTxUdpSocket(const IPADDR &send_to_addr, uint16_t remote_port, uint16_t local_port)
Create transmit-only UDP socket for sending to specified destination.
Definition udp.h:5563
Scenario 2: Echo Server Without Bidirectional Socket
uint8_t buf[512];
uint16_t lp, rp;
int n =
recvfrom(sock, buf,
sizeof(buf), &clientIP, &lp, &rp);
sendto(sock, buf, n, clientIP, rp);
int n =
recvfrom(sock, buf,
sizeof(buf), &clientIP, &lp, &rp);
sendto(sock, buf, n, clientIP, rp);
int CreateRxTxUdpSocket(const IPADDR &send_to_addr, uint16_t send_to_remote_port, uint16_t local_port)
Create bidirectional UDP socket for both sending and receiving.
Definition udp.h:5748
Solution Patterns:
Pattern 1: Use Correct Socket Type
Pattern 2: Separate Sockets for Send/Receive
recvfrom(rxSock, buf, len, &srcIP, &lp, &rp);
sendto(txSock, data, dataLen, destIP, destPort);
- Warning
- This error does NOT mean the socket descriptor is invalid. The socket exists and may be used for receiving, just not sending.
UDP_ERR_NOTOPEN_TO_READ
Socket not configured for receiving operations
#define UDP_ERR_NOTOPEN_TO_READ (-3)
Returned when attempting to receive data from a socket that was created in transmit-only mode (using CreateTxUdpSocket()). The socket can only send packets, not receive them.
Common Scenarios:**
Scenario 1: Receiving on Transmit-Only Socket**
IPADDR serverIP = AsciiToIp(
"192.168.1.100");
uint8_t buffer[512];
uint16_t lp, rp;
int n =
recvfrom(sock, buffer,
sizeof(buffer), &srcIP, &lp, &rp);
int n =
recvfrom(sock, buffer,
sizeof(buffer), &srcIP, &lp, &rp);
Scenario 2: Request/Response Without Bidirectional Socket**
IPADDR serverIP = AsciiToIp(
"10.0.1.50");
uint8_t request[] = "GET /data";
sendto(sock, request,
sizeof(request), serverIP, 6000);
uint8_t response[512];
uint16_t lp, rp;
int n =
recvfrom(sock, response,
sizeof(response), &respIP, &lp, &rp);
sendto(sock, request,
sizeof(request), serverIP, 6000);
int n =
recvfrom(sock, response,
sizeof(response), &respIP, &lp, &rp);
Scenario 3: DNS Query (Typical Request/Response)**
IPADDR dnsServer = AsciiToIp(
"8.8.8.8");
uint8_t query[512];
int qlen = BuildDnsQuery(query, "example.com");
sendto(sock, query, qlen, dnsServer, 53);
uint8_t response[512];
uint16_t lp, rp;
int rlen =
recvfrom(sock, response,
sizeof(response), &srcIP, &lp, &rp);
Solution Patterns:**
Pattern 1: Choose Appropriate Socket Type**
- Warning
- This error does NOT mean the socket descriptor is invalid. The socket exists and may be used for sending, just not receiving.
- Note
- If you need truly one-way fire-and-forget communication, CreateTxUdpSocket() is more efficient than CreateRxTxUdpSocket() because it doesn't allocate receive buffers. However, most protocols benefit from at least basic acknowledgment, making bidirectional sockets the better choice.
- See also
- CreateRxUdpSocket() - For sockets that can receive
-
CreateRxTxUdpSocket() - For bidirectional communication
-
recvfrom() - Function that may return this error
Error Handling Pattern
Always check return values from UDP socket functions:
if (sock < 0) {
switch (sock) {
printf("Socket does not exist or creation failed\r\n");
break;
printf("Socket not configured for sending\r\n");
break;
printf("Socket not configured for receiving\r\n");
break;
default:
printf("Unknown error: %d\r\n", sock);
break;
}
return;
}
uint8_t buffer[1024];
uint16_t lp, rp;
int bytesReceived =
recvfrom(sock, buffer,
sizeof(buffer), &srcIP, &lp, &rp);
#define UDP_ERR_NOTOPEN_TO_READ
Socket not open for read.
Definition udp.h:4764
#define UDP_ERR_NOSUCH_SOCKET
Socket does not exist.
Definition udp.h:4762
Common Causes and Solutions
UDP_ERR_NOSUCH_SOCKET
Causes:
- Port already in use by another socket
- System resource exhaustion (too many sockets)
- Invalid socket descriptor passed to function
- Socket was closed but still being referenced
Solutions:
- Check that port is not already bound
- Close unused sockets to free resources
- Verify socket is valid before operations
- Use different port numbers for different sockets
UDP_ERR_NOTOPEN_TO_WRITE
Causes
sendto(rxSock, data, len, destIP, destPort);
sendto(txSock, data, len, destIP, destPort);
sendto(rxtxSock, data, len, destIP, destPort);
UDP_ERR_NOTOPEN_TO_READ
Causes
IPADDR serverIP = AsciiToIp(
"192.168.1.100");
recvfrom(txSock, buffer, len, &srcIP, &lp, &rp);
recvfrom(rxSock, buffer, len, &srcIP, &lp, &rp);
sendto(rxtxSock, request, reqLen, serverIP, 8080);
recvfrom(rxtxSock, buffer, len, &srcIP, &lp, &rp);
Error Checking Best Practices
1. Always Validate Socket Creation
if (sock < 0) {
printf("Socket creation failed: %d\r\n", sock);
return ERROR_CODE;
}
2. Check Operation Return Values
int bytesSent =
sendto(sock, data, len, destIP, destPort);
if (bytesSent < 0) {
printf("Send failed: error %d\r\n", bytesSent);
} else if (bytesSent != len) {
printf("Warning: Partial send (%d of %d bytes)\r\n", bytesSent, len);
}
3. Defensive Programming
if (result < 0) {
printf("CreateRxUdpSocket(%d) failed with error %d at %s:%d\r\n",
port, result, __FILE__, __LINE__);
switch (result) {
break;
}
}
4. Resource Cleanup on Error
if (sock1 < 0) {
return ERROR_SOCKET1;
}
if (sock2 < 0) {
return ERROR_SOCKET2;
}
Debugging Errors
When encountering UDP errors:
- Enable System Diagnostics
void UserMain(void* pd) {
}
void EnableSystemDiagnostics()
Turn on the diagnostic reports from the config page.
- Check Resource Availability
- Monitor socket count
- Check buffer pool usage
- Verify network interface status
- Test with Simple Cases
- Start with single socket
- Verify basic send/receive works
- Add complexity incrementally
- Use Network Tools
- Wireshark to capture packets
- netcat (nc) to test connectivity
- Verify firewall settings
Related Functions
Functions that may return these error codes:
UDP Sockets Example
This example demonstrates UDP communication using socket functions instead of the UDPPacket class.
UDP Sockets Send/Receive Example
Features:
- Bidirectional UDP communication
- Task-based receive handling
- Main loop for sending
#include <init.h>
#include <stdlib.h>
#include <string.h>
#include <system.h>
#include <udp.h>
#include <utils.h>
const char *AppName = "UDP Sockets Example";
uint32_t UdpReceiveTaskStack[USER_TASK_STK_SIZE];
void UdpReceiveTask(void *pd)
{
int listenPort = (int)pd;
printf("UdpReceiveTask monitoring port %d\r\n", listenPort);
if (udpFd <= 0)
{
printf("Error Creating UDP Listen Socket: %d\r\n", udpFd);
while (1)
{
}
}
else
{
printf("Listening for UDP packets on port %d\r\n", listenPort);
}
while (1)
{
uint16_t localPort;
uint16_t sourcePort;
char buffer[80];
int len =
recvfrom(udpFd, (uint8_t *)buffer, 80,
&sourceIpAddress, &localPort, &sourcePort);
buffer[len] = '\0';
printf("\r\nReceived a UDP packet with %d bytes from: %I\r\n%s\r\n",
len, sourceIpAddress, buffer);
}
}
void UserMain(void *pd)
{
int portNumber;
char buffer[80];
printf(
"Application: %s\r\nNNDK Revision: %s\r\n", AppName,
GetReleaseTag());
printf("Enter the UDP Server destination IP address: ");
gets(buffer);
destIpAddress = AsciiToIp(buffer);
printf("\r\n");
printf("Enter the source/destination port number: ");
gets(buffer);
portNumber = atoi(buffer);
printf("\r\n");
if (udpFd <= 0)
{
printf("Error Creating UDP Socket: %d\r\n", udpFd);
while (1)
{
}
}
else
{
printf("Sending/Receiving with host %I:%d\r\n", destIpAddress, portNumber);
}
OSTaskCreatewName(UdpReceiveTask,
(void *)portNumber,
&UdpReceiveTaskStack[USER_TASK_STK_SIZE],
UdpReceiveTaskStack,
"UDP Receive");
printf("Enter data and hit enter to send.\r\n");
while (1)
{
gets(buffer);
printf("\r\n");
printf("Sending \"%s\" using UDP to %I:%d\r\n", buffer, destIpAddress, portNumber);
sendto(udpFd, (uint8_t *)buffer, strlen(buffer), destIpAddress, portNumber);
printf("\r\n");
}
}
Socket Function Reference
Receiving**:
uint16_t sourcePort, destPort;
char buffer[SIZE];
int len =
recvfrom(udpFd, (uint8_t*)buffer, SIZE,
&sourceIP, &destPort, &sourcePort);
Sending**:
sendto(udpFd, (uint8_t*)buffer, length, destIP, destPort);
UDP Socket Architecture
Socket-Based UDP Flow:
Receive Path: Send Path:
┌──────────────────┐ ┌───────────────────┐
│ (port) │ │ (ip, dport, sport)│
└────────┬─────────┘ └────────┬──────────┘
│ │
v v
┌──────────────────┐ ┌──────────────────┐
│ [BLOCKING] │ │ (immediate) │
└────────┬─────────┘ └────────┬─────────┘
│ │
v v
┌──────────────────┐ ┌──────────────────┐
│ Extract data │ │ Packet sent │
│ from buffer │ │ to network │
└──────────────────┘ └──────────────────┘
Best Practices Summary
General Guidelines
- Protocol Selection:
- Choose TCP for reliability and ordered delivery
- Choose UDP for speed and efficiency
- Consider application-layer reliability over UDP when appropriate
- Resource Management:
- Always close sockets when done
- Check return values from all network functions
- Handle connection errors gracefully
- Task Architecture:
- Use separate tasks for listening/receiving
- Set appropriate task priorities
- Consider using
select()
for multiple connections
- Buffer Management:
- Allocate adequate buffer sizes
- Null-terminate string data
- Use static buffers in tasks to save stack space
- Error Handling:
- Check socket creation return values
- Handle connection failures
- Implement timeout mechanisms
Performance Optimization
- Use
select()
for multiple connections (TCP)
- Consider OS_FIFO size for UDP packet bursts
- Set appropriate task priorities for responsive I/O
- Use
ReadWithTimeout()
for connection management
Security Considerations
- Validate all received data
- Implement authentication when needed
- Use random source port numbers
- Consider connection limits for servers