Contributed by WildboarG, organized by Ai-Thinker
[Ai-WB2 Advanced] UDP Wireless Communication
Last edited by WildboarG at 2024-9-13 17:09 Last edited by WildboarG at 2024-9-13 16:36 Last edited by WildboarG at 2024-9-13 16:34 Last edited by WildboarG at 2024-9-13 16:10 Last edited by WildboarG at 2024-9-13 14:19
- Overview of UDP (User Datagram Protocol) UDP is a transport layer protocol. Its function is to add the most basic services on top of the IP datagram service (by introducing the concept of ports): multiplexing and demultiplexing, and error detection. UDP provides unreliable service but has advantages that TCP does not:
- UDP is connectionless, so in terms of time there is no delay required for connection establishment. In terms of space, TCP needs to maintain connection state in the end systems, which requires certain overhead. This connection state includes receive and send buffers, congestion control parameters, and sequence number and acknowledgment number parameters. UDP does not maintain connection state or track these parameters, so its overhead is small. It has advantages in both space and time. For example: If DNS ran over TCP instead of UDP, DNS would be much slower. HTTP uses TCP rather than UDP because reliability is important for text-based web pages. The same dedicated application server can definitely support more active clients when it supports UDP.
- Small packet header overhead: the TCP header is 20 bytes, while the UDP header is only 8 bytes.
- UDP has no congestion control, so the application layer can better control the data to be sent and when to send it, and congestion control in the network will not affect the host's sending rate. Some real-time applications need to send at a steady rate and can tolerate some data loss, but cannot tolerate large delays (e.g., real-time video, live streaming, etc.)
- UDP provides best-effort delivery and does not guarantee reliable delivery. All the work of maintaining transmission reliability must be done by the user at the application layer. There is no acknowledgment mechanism or retransmission mechanism as in TCP. If the packet is not delivered to the peer due to network reasons, UDP will not return an error message to the application layer either
- UDP is message-oriented: after adding a header to the message handed down by the application layer, it delivers it directly to the IP layer without merging or splitting, preserving the boundaries of these messages. When a UDP user datagram is handed up from the IP layer, the header is removed and the datagram is delivered unchanged to the upper-layer application process. A message cannot be divided; it is the smallest unit processed by UDP datagrams. This is exactly why UDP is not flexible enough: it cannot control the number and amount of reads/writes of data. For example, if we want to send a 100-byte message, calling the sendto function once sends all 100 bytes, and the peer also needs to receive all 100 bytes at once with recvfrom. You cannot use a loop to fetch 10 bytes each time, ten times.
- UDP is commonly used in network applications that transmit relatively small amounts of data at a time, such as DNS, SNMP, etc., because for these applications, using TCP would bring considerable overhead for connection creation, maintenance, and teardown. UDP is also often used in multimedia applications (such as IP telephony, real-time video conferencing, streaming media, etc.), where reliable transmission of data is not important to them, and TCP's congestion control would cause them large delays that are also intolerable
- The UDP Header Format A UDP datagram consists of a header and a user data part. The entire UDP datagram is encapsulated in an IP datagram as the data part of the IP datagram. The structure of a UDP datagram is shown in the figure: ,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70),type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70)
The UDP header is 8 bytes long and consists of 4 fields, each of which is 2 bytes: 1.Source port: the source port number (used when a reply from the peer is needed; set to all 0s when not needed). 2.Destination port: the destination port number, i.e., who it is sent to (needed when delivering the message at the destination). 3.Length: the length of the UDP datagram (including the header and data); its minimum value is 8 (header only) 4.Checksum: detects whether the UDP datagram has errors during transmission; if there are errors, it is discarded. This field is optional. When the source host does not want to compute the checksum, it simply sets this field to all 0s. When the transport layer receives a UDP datagram from the IP layer, it delivers the UDP datagram to the application process through the corresponding port according to the destination port in the header. If the receiving UDP finds that the destination port number in the received message is incorrect (there is no application process with the corresponding port number), it discards the message and ICMP sends a "port unreachable" error message to the sender. - UDP Checksum When computing the checksum, a 12-byte pseudo-header needs to be prepended to the UDP datagram. The pseudo-header is not the real UDP header; it is only temporarily added in front of the UDP datagram for computing the checksum. The pseudo-header is not sent; instead, a temporary UDP datagram is obtained, and the checksum is computed over this temporary UDP datagram. The pseudo-header is neither transmitted downward nor delivered upward; it exists only to compute the checksum. Such a checksum checks not only the UDP datagram but also the source and destination IP addresses of the IP datagram.
The calculation method of the UDP checksum is similar to that of the IP datagram header checksum: both use one's complement arithmetic to sum and then invert. The difference is that the IP datagram checksum only checks the IP datagram header, while the UDP checksum checks the header and the data part together. For the sender: - First, put all zeros in the checksum field and add the pseudo-header,
- Then treat the UDP datagram as many 16-bit substrings joined together. If the data part of the UDP datagram is not an even number of bytes, add a zero byte at the end of the data part (this byte is not sent)
- Next, compute the sum of these 16-bit words using one's complement arithmetic, and write the one's complement of this sum into the checksum field. On the receiving side, add the pseudo-header to the received UDP datagram (pad with a zero byte if the length is not an even number of bytes), and then compute the sum of these 16-bit words using one's complement arithmetic. When there is no error, the result is all 1s. Otherwise, an error has occurred and the receiver should discard the UDP datagram.
- Note that the content of the pseudo-header is not counted in the UDP length. Take the following figure as an example: UDP header 8 bytes + UDP data 7 bytes = 15 bytes in total (the padded zero is not counted in the UDP data length)
,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70) Note: - When checking, if the length of the UDP datagram part is not an even number of bytes, a zero byte needs to be filled in, but like the pseudo-header, this byte is not sent.
- If the UDP checksum verifies that the UDP datagram is erroneous, it can be discarded or delivered to the upper layer, but an error report must be attached to tell the upper layer that this is an erroneous datagram.
- Through the pseudo-header, not only the source port number, the destination port number, and the data part of the UDP user datagram can be checked, but also the source and destination IP addresses of the IP datagram. This kind of error detection is not very strong in error-detecting capability, but it is simple and fast. Like TCP, UDP is also a transport layer protocol. In network data exchange, whether over Ethernet or Wi-Fi, it is still part of a complete frame. When data enters the transport layer, the corresponding header is added or removed.

UDP-Based Application Layer Protocols
DNS (Domain Name Service): the DNS protocol is used to convert domain names into IP addresses. It uses UDP port 53, because DNS queries are usually very short, and UDP is lightweight and can process these queries quickly. TFTP (Trivial File Transfer Protocol): TFTP is a simple file transfer protocol that uses UDP port 69. Although TFTP is not as powerful as FTP, due to its simplicity and ease of implementation, it is still useful in some occasions that require fast and simple file transfers. SNMP (Simple Network Management Protocol): SNMP is used for monitoring and managing network devices. It usually uses UDP port 161 to receive requests, while trap messages use UDP port 162. NTP (Network Time Protocol): NTP is used to synchronize computer clocks to a reference time source, usually a reliable time server. It uses UDP port 123. VoIP (Voice over IP): some VoIP protocols (such as RTP) use UDP for real-time transmission of audio data. UDP's connectionless and fast transmission characteristics make it suitable for real-time communications such as audio and video streaming. DHCP (Dynamic Host Configuration Protocol): DHCP uses UDP for communication between the client and the DHCP server, to automatically assign IP addresses and other network settings. QUIC (Quick UDP Internet Connections): although QUIC is based on UDP, it is more like a transport layer protocol itself, providing the application layer with reliable transmission services similar to TCP while maintaining UDP's low-latency characteristics. These protocols choose UDP over TCP usually because they require low latency, simplicity, or high real-time performance of data transmission, and can tolerate a certain degree of data loss or out-of-order arrival. Note that although UDP provides the required characteristics for these applications, in practice they may also need to implement additional mechanisms at the application layer to ensure data reliability or integrity Same as TCP, the WB2 SDK also uses the socket interface to control the UDP PCB; you just need to specify the stream type as SOCK_DGRAM when creating the socket Of course, UDP also requires initialization, connection, sending, receiving, and closing The official SDK also provides corresponding example programs:
- Connecting to another UDP service as a client (unicast)
- Starting a UDP service and waiting for client connections
- Broadcast
- Multicast Let me also add the concepts of broadcast and multicast: The difference between broadcast UDP and unicast UDP is simply the IP address: broadcast uses the broadcast address 255.255.255.255 and sends the message to every host on the same broadcast network. It is worth emphasizing: local broadcast messages are not forwarded by routers. Of course, this is very easy to understand, because if routers forwarded broadcast messages, it would inevitably paralyze the network. This is also why the designers of the IP protocol deliberately did not define an Internet-wide broadcast mechanism. Broadcast addresses are commonly used, for example, for players on the same local network in online games to exchange status information with each other. Actually, as the name suggests, broadcasting means speaking to everyone on the LAN, but broadcast still needs to specify the receiver's port number, because it is impossible for all the ports of the receiver to listen to the broadcast. Multicast, also called "group cast", logically groups hosts of the same service type in the network. When sending and receiving data, the data only travels within the same group; other hosts that have not joined the group cannot send or receive the corresponding data. When multicasting over a wide area network, switches and routers only replicate and forward data to the hosts that need to obtain the data. A host can request to join or leave a group from the router. Routers and switches in the network selectively replicate and transmit data, delivering it only to the hosts in the group. This multicast capability can send data to multiple hosts at once while ensuring that it does not affect the other communications of hosts that do not need it (have not joined the group). Compared with traditional one-to-one unicast, multicast has the following advantages: 1. Hosts with the same service join the same data stream and share the same channel, saving bandwidth and server resources. It has the advantages of broadcast without the bandwidth that broadcast requires. 2. The server's total bandwidth is not limited by the client bandwidth. Since the multicast protocol determines whether to forward the data stream based on the receiver's needs, the server-side bandwidth is constant, independent of the number of clients. 3. Like unicast, multicast is allowed to be transmitted over wide area networks, i.e., the Internet, while broadcast can only be performed on the same local area network. Disadvantages of multicast: 1. Compared with unicast, multicast has no error correction mechanism; when an error occurs it is hard to remedy, but this function can be implemented at the application layer. 2. Multicast network support has defects and requires the support of routers and the network protocol stack. 3. Multicast applications mainly include online video, online conferencing, etc.
2. Multicast over Wide Area Networks
Multicast addresses are specific: Class D addresses are used for multicast. Class D IP addresses are multicast IP addresses, i.e., IP addresses between 224.0.0.0 and 239.255.255.255, which are divided into three categories: local network control multicast addresses, reserved multicast addresses, and administratively scoped multicast addresses: 1. Local multicast addresses: between 224.0.0.0 and 224.0.0.255. These are reserved for routing protocols and other purposes; routers do not forward IP packets in this range. 2. Reserved multicast addresses: between 224.0.1.0 and 238.255.255.255, usable globally (e.g., the Internet) or for network protocols. 3. Administratively scoped multicast addresses: between 239.0.0.0 and 239.255.255.255, for use within an organization, similar to private IP addresses. They cannot be used on the Internet and can limit the multicast scope. Multicast programming is implemented with the setsockopt() function and the getsockopt() function Connecting to a UDP service as a client: Create a UDP server with a serial port assistant or a script, and flash the client example provided by the official SDK. To verify the UDP client, see the comments section for the effect screenshots
Click to expand full code
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include <blog.h>
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <cli.h>
#include <hal_wifi.h>
#include <lwip/init.h>
#include "udp_client.h"
#define ROUTER_SSID "CU_e6f6"
#define ROUTER_PWD "c9g3geyu"
#define UDP_SERVER_IP "192.168.1.8"
#define UDP_SERVER_PORT 12345
static wifi_conf_t conf = {
.country_code = "CN",
};
/**
* @brief wifi_sta_connect
* wifi station mode connect start
* @param ssid
* @param password
*/
static void wifi_sta_connect(char* ssid, char* password)
{
wifi_interface_t wifi_interface;
wifi_interface = wifi_mgmr_sta_enable();
wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}
/**
* @brief udp_client_task
*
* @param arg
*/
static void udp_client_task(void* arg)
{
blog_info("udp client task run\r\n");
int socketfd;
int ret = 0;
char* tcp_buff = pvPortMalloc(512);
memset(tcp_buff, 0, 512);
socketfd = udp_client_init(UDP_SERVER_IP, UDP_SERVER_PORT);
if (!udp_client_connect(socketfd)) {
blog_info("%s:udp client connect OK\r\n", __func__);
}
else goto __exit;
if (udp_client_send(socketfd, "hell udp server")<0) {
printf("udp client send fail\r\n");
goto __exit;
}
else
blog_info("udp client send OK\r\n");
while (1) {
ret = udp_client_receive(socketfd, tcp_buff);
if (ret>0) {
blog_info("%s:udp receive data:%s \r\n", __func__, tcp_buff);
if (strstr(tcp_buff, "close")) goto __exit;
memset(tcp_buff, 0, 512);
}
vTaskDelay(100/portTICK_PERIOD_MS);
}
__exit:
vPortFree(tcp_buff);
udp_client_send(socketfd, "client close");
udp_client_deinit(socketfd);
vTaskDelete(NULL);
}
/**
* @brief event_cb_wifi_event
* wifi connet ap event Callback function
* @param event
* @param private_data
*/
static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
static char* ssid;
static char* password;
switch (event->code)
{
case CODE_WIFI_ON_INIT_DONE:
{
printf("[APP] [EVT] INIT DONE %lld\r\n", aos_now_ms());
wifi_mgmr_start_background(&conf);
}
break;
case CODE_WIFI_ON_MGMR_DONE:
{
printf("[APP] [EVT] MGMR DONE %lld\r\n", aos_now_ms());
//_connect_wifi();
wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
}
break;
case CODE_WIFI_ON_SCAN_DONE:
{
printf("[APP] [EVT] SCAN Done %lld\r\n", aos_now_ms());
// wifi_mgmr_cli_scanlist();
}
break;
case CODE_WIFI_ON_DISCONNECT:
{
printf("[APP] [EVT] disconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTING:
{
printf("[APP] [EVT] Connecting %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_CMD_RECONNECT:
{
printf("[APP] [EVT] Reconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTED:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_PRE_GOT_IP:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_GOT_IP:
{
printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
printf("[SYS] Memory left is %d Bytes\r\n", xPortGetFreeHeapSize());
//WiFi connection succeeded, create UDP client task
xTaskCreate(udp_client_task, (char*)"udp_client_task", 1024*2, NULL, 16, NULL);
}
break;
case CODE_WIFI_ON_PROV_SSID:
{
printf("[APP] [EVT] [PROV] [SSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (ssid)
{
vPortFree(ssid);
ssid = NULL;
}
ssid = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_BSSID:
{
printf("[APP] [EVT] [PROV] [BSSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (event->value)
{
vPortFree((void*)event->value);
}
break;
case CODE_WIFI_ON_PROV_PASSWD:
{
printf("[APP] [EVT] [PROV] [PASSWD] %lld: %s\r\n", aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (password)
{
vPortFree(password);
password = NULL;
}
password = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_CONNECT:
{
printf("[APP] [EVT] [PROV] [CONNECT] %lld\r\n", aos_now_ms());
printf("connecting to %s:%s...\r\n", ssid, password);
wifi_sta_connect(ssid, password);
}
break;
case CODE_WIFI_ON_PROV_DISCONNECT:
{
printf("[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n", aos_now_ms());
}
break;
default:
{
printf("[APP] [EVT] Unknown code %u, %lld\r\n", event->code, aos_now_ms());
/*nothing*/
}
static void proc_main_entry(void* pvParameters)
{
aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
hal_wifi_start_firmware_task();
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
vTaskDelete(NULL);
}
void main()
{
puts("[OS] Starting TCP/IP Stack...\r\n");
tcpip_init(NULL, NULL);
puts("[OS] proc_main_entry task...\r\n");
xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}Start the UDP server on the WB2 and use a script as the client to connect (see the comments section for the effect screenshots)
Click to expand full code
/**
* @file main.c
* @author your name ([email]you@domain.com[/email])
* @brief
* @version 0.1
* @date 2022-10-13
*
* @copyright Copyright (c) 2022
*
*/
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include <blog.h>
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <cli.h>
#include <hal_wifi.h>
#include <lwip/init.h>
#include "udp_server.h"
#define ROUTER_SSID "CU_e6f6"
#define ROUTER_PWD "c9g3geyu"
#define UDP_SERVER_PORT 7878
static wifi_conf_t conf = {
.country_code = "CN",
};
/**
* @brief wifi_sta_connect
* wifi station mode connect start
* @param ssid
* @param password
*/
static void wifi_sta_connect(char* ssid, char* password)
{
wifi_interface_t wifi_interface;
wifi_interface = wifi_mgmr_sta_enable();
wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}
/**
* @brief udp_server_task
*
* @param arg
*/
static void udp_server_task(void* arg)
{
int socket_fd;
int ret = 0;
socket_fd = udp_server_init(NULL, UDP_SERVER_PORT);
struct sockaddr_in u_sockaddr;
int socke_len = sizeof(struct sockaddr_in);
char* udp_buf = pvPortMalloc(512);
while (1) {
memset(udp_buf, 0, 512);
ret = recvfrom(socket_fd, udp_buf, 512, MSG_DONTWAIT, (struct sockaddr*)&u_sockaddr, (socklen_t*)&socke_len);
if (ret>0) {
blog_info("%s:%s\r\n", inet_ntoa(u_sockaddr.sin_addr.s_addr), udp_buf);
ret = sendto(socket_fd, udp_buf, strlen(udp_buf), 0, (struct sockaddr*)&u_sockaddr, socke_len);
if (strstr(udp_buf, "close")!=NULL) {
ret = sendto(socket_fd, "server close connect", strlen("server close connect"), 0, (struct sockaddr*)&u_sockaddr, socke_len);
goto __exit;
}
vTaskDelay(50/portTICK_PERIOD_MS);
}
__exit:
vPortFree(udp_buf);
udp_server_deinit();
vTaskDelete(NULL);
}
/**
* @brief event_cb_wifi_event
* wifi connet ap event Callback function
* @param event
* @param private_data
*/
static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
static char* ssid;
static char* password;
switch (event->code)
{
case CODE_WIFI_ON_INIT_DONE:
{
printf("[APP] [EVT] INIT DONE %lld\r\n", aos_now_ms());
wifi_mgmr_start_background(&conf);
}
break;
case CODE_WIFI_ON_MGMR_DONE:
{
printf("[APP] [EVT] MGMR DONE %lld\r\n", aos_now_ms());
//_connect_wifi();
wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
}
break;
case CODE_WIFI_ON_SCAN_DONE:
{
printf("[APP] [EVT] SCAN Done %lld\r\n", aos_now_ms());
// wifi_mgmr_cli_scanlist();
}
break;
case CODE_WIFI_ON_DISCONNECT:
{
printf("[APP] [EVT] disconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTING:
{
printf("[APP] [EVT] Connecting %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_CMD_RECONNECT:
{
printf("[APP] [EVT] Reconnect %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_CONNECTED:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_PRE_GOT_IP:
{
printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
}
break;
case CODE_WIFI_ON_GOT_IP:
{
printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
printf("[SYS] Memory left is %d Bytes\r\n", xPortGetFreeHeapSize());
// wifi connection succeeded, create udp server task
xTaskCreate(udp_server_task, "udp_server_task", 2048, NULL, 16, NULL);
}
break;
case CODE_WIFI_ON_PROV_SSID:
{
printf("[APP] [EVT] [PROV] [SSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (ssid)
{
vPortFree(ssid);
ssid = NULL;
}
ssid = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_BSSID:
{
printf("[APP] [EVT] [PROV] [BSSID] %lld: %s\r\n",
aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (event->value)
{
vPortFree((void*)event->value);
}
break;
case CODE_WIFI_ON_PROV_PASSWD:
{
printf("[APP] [EVT] [PROV] [PASSWD] %lld: %s\r\n", aos_now_ms(),
event->value ? (const char*)event->value : "UNKNOWN");
if (password)
{
vPortFree(password);
password = NULL;
}
password = (char*)event->value;
}
break;
case CODE_WIFI_ON_PROV_CONNECT:
{
printf("[APP] [EVT] [PROV] [CONNECT] %lld\r\n", aos_now_ms());
printf("connecting to %s:%s...\r\n", ssid, password);
wifi_sta_connect(ssid, password);
}
break;
case CODE_WIFI_ON_PROV_DISCONNECT:
{
printf("[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n", aos_now_ms());
}
break;
default:
{
printf("[APP] [EVT] Unknown code %u, %lld\r\n", event->code, aos_now_ms());
/*nothing*/
}
static void proc_main_entry(void* pvParameters)
{
aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
hal_wifi_start_firmware_task();
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
vTaskDelete(NULL);
}
void main()
{
puts("[OS] Starting TCP/IP Stack...\r\n");
tcpip_init(NULL, NULL);
puts("[OS] proc_main_entry task...\r\n");
xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}Regarding broadcast: in the broadcast example, since the broadcast address is set to the local machine's IP (i.e., the IP obtained by the WB2), it can only receive its own broadcast. When I tried setting the broadcast address to 192.168.1.255 or 255.255.255.255, it did not work. Does anyone know what is going on? At first I suspected the router was rejecting broadcasts, but then I wrote a test script and found that broadcast reception still worked in the test example program. I cannot figure it out; I will wait for the experts in the comments section to explain.
Last edited by WildboarG at 2024-9-13 17:09
Last edited by WildboarG at 2024-9-13 17:09
Last edited by WildboarG at 2024-9-13 16:36
Last edited by WildboarG at 2024-9-13 16:34
Last edited by WildboarG at 2024-9-13 16:10
Last edited by WildboarG at 2024-9-13 14:19
- Overview of UDP (User Datagram Protocol)
UDP is a transport layer protocol. Its function is to add the most basic services on top of the IP datagram service (by introducing the concept of ports): multiplexing and demultiplexing, and error detection.
UDP provides unreliable service but has advantages that TCP does not:
UDP is connectionless, so in terms of time there is no delay required for connection establishment. In terms of space, TCP needs to maintain connection state in the end systems, which requires certain overhead. This connection state includes receive and send buffers, congestion control parameters, and sequence number and acknowledgment number parameters. UDP does not maintain connection state or track these parameters, so its overhead is small. It has advantages in both space and time. For example:
If DNS ran over TCP instead of UDP, DNS would be much slower. HTTP uses TCP rather than UDP because reliability is important for text-based web pages. The same dedicated application server can definitely support more active clients when it supports UDP.
Small packet header overhead: the TCP header is 20 bytes, while the UDP header is only 8 bytes.
UDP has no congestion control, so the application layer can better control the data to be sent and when to send it, and congestion control in the network will not affect the host's sending rate. Some real-time applications need to send at a steady rate and can tolerate some data loss, but cannot tolerate large delays (e.g., real-time video, live streaming, etc.)
UDP provides best-effort delivery and does not guarantee reliable delivery. All the work of maintaining transmission reliability must be done by the user at the application layer. There is no acknowledgment mechanism or retransmission mechanism as in TCP. If the packet is not delivered to the peer due to network reasons, UDP will not return an error message to the application layer either
UDP is message-oriented: after adding a header to the message handed down by the application layer, it delivers it directly to the IP layer without merging or splitting, preserving the boundaries of these messages. When a UDP user datagram is handed up from the IP layer, the header is removed and the datagram is delivered unchanged to the upper-layer application process. A message cannot be divided; it is the smallest unit processed by UDP datagrams. This is exactly why UDP is not flexible enough: it cannot control the number and amount of reads/writes of data. For example, if we want to send a 100-byte message, calling the sendto function once sends all 100 bytes, and the peer also needs to receive all 100 bytes at once with recvfrom. You cannot use a loop to fetch 10 bytes each time, ten times.
UDP is commonly used in network applications that transmit relatively small amounts of data at a time, such as DNS, SNMP, etc., because for these applications, using TCP would bring considerable overhead for connection creation, maintenance, and teardown. UDP is also often used in multimedia applications (such as IP telephony, real-time video conferencing, streaming media, etc.), where reliable transmission of data is not important to them, and TCP's congestion control would cause them large delays that are also intolerable
- The UDP Header Format
A UDP datagram consists of a header and a user data part. The entire UDP datagram is encapsulated in an IP datagram as the data part of the IP datagram. The structure of a UDP datagram is shown in the figure:
,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70),type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70) 
The UDP header is 8 bytes long and consists of 4 fields, each of which is 2 bytes: 1.Source port: the source port number (used when a reply from the peer is needed; set to all 0s when not needed). 2.Destination port: the destination port number, i.e., who it is sent to (needed when delivering the message at the destination). 3.Length: the length of the UDP datagram (including the header and data); its minimum value is 8 (header only) 4.Checksum: detects whether the UDP datagram has errors during transmission; if there are errors, it is discarded. This field is optional. When the source host does not want to compute the checksum, it simply sets this field to all 0s. When the transport layer receives a UDP datagram from the IP layer, it delivers the UDP datagram to the application process through the corresponding port according to the destination port in the header. If the receiving UDP finds that the destination port number in the received message is incorrect (there is no application process with the corresponding port number), it discards the message and ICMP sends a "port unreachable" error message to the sender.
- UDP Checksum
When computing the checksum, a 12-byte pseudo-header needs to be prepended to the UDP datagram. The pseudo-header is not the real UDP header; it is only temporarily added in front of the UDP datagram for computing the checksum. The pseudo-header is not sent; instead, a temporary UDP datagram is obtained, and the checksum is computed over this temporary UDP datagram. The pseudo-header is neither transmitted downward nor delivered upward; it exists only to compute the checksum. Such a checksum checks not only the UDP datagram but also the source and destination IP addresses of the IP datagram.

The calculation method of the UDP checksum is similar to that of the IP datagram header checksum: both use one's complement arithmetic to sum and then invert. The difference is that the IP datagram checksum only checks the IP datagram header, while the UDP checksum checks the header and the data part together.
For the sender:
- First, put all zeros in the checksum field and add the pseudo-header,
- Then treat the UDP datagram as many 16-bit substrings joined together. If the data part of the UDP datagram is not an even number of bytes, add a zero byte at the end of the data part (this byte is not sent)
- Next, compute the sum of these 16-bit words using one's complement arithmetic, and write the one's complement of this sum into the checksum field. On the receiving side, add the pseudo-header to the received UDP datagram (pad with a zero byte if the length is not an even number of bytes), and then compute the sum of these 16-bit words using one's complement arithmetic. When there is no error, the result is all 1s. Otherwise, an error has occurred and the receiver should discard the UDP datagram.
- Note that the content of the pseudo-header is not counted in the UDP length. Take the following figure as an example: UDP header 8 bytes + UDP data 7 bytes = 15 bytes in total (the padded zero is not counted in the UDP data length)

,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMTkyODk5Mjc3Mg==,size_16,color_FFFFFF,t_70)
Note:
- When checking, if the length of the UDP datagram part is not an even number of bytes, a zero byte needs to be filled in, but like the pseudo-header, this byte is not sent.
- If the UDP checksum verifies that the UDP datagram is erroneous, it can be discarded or delivered to the upper layer, but an error report must be attached to tell the upper layer that this is an erroneous datagram.
- Through the pseudo-header, not only the source port number, the destination port number, and the data part of the UDP user datagram can be checked, but also the source and destination IP addresses of the IP datagram. This kind of error detection is not very strong in error-detecting capability, but it is simple and fast.
Like TCP, UDP is also a transport layer protocol. In network data exchange, whether over Ethernet or Wi-Fi, it is still part of a complete frame. When data enters the transport layer, the corresponding header is added or removed.

UDP-Based Application Layer Protocols
DNS (Domain Name Service): the DNS protocol is used to convert domain names into IP addresses. It uses UDP port 53, because DNS queries are usually very short, and UDP is lightweight and can process these queries quickly. TFTP (Trivial File Transfer Protocol): TFTP is a simple file transfer protocol that uses UDP port 69. Although TFTP is not as powerful as FTP, due to its simplicity and ease of implementation, it is still useful in some occasions that require fast and simple file transfers. SNMP (Simple Network Management Protocol): SNMP is used for monitoring and managing network devices. It usually uses UDP port 161 to receive requests, while trap messages use UDP port 162. NTP (Network Time Protocol): NTP is used to synchronize computer clocks to a reference time source, usually a reliable time server. It uses UDP port 123. VoIP (Voice over IP): some VoIP protocols (such as RTP) use UDP for real-time transmission of audio data. UDP's connectionless and fast transmission characteristics make it suitable for real-time communications such as audio and video streaming. DHCP (Dynamic Host Configuration Protocol): DHCP uses UDP for communication between the client and the DHCP server, to automatically assign IP addresses and other network settings. QUIC (Quick UDP Internet Connections): although QUIC is based on UDP, it is more like a transport layer protocol itself, providing the application layer with reliable transmission services similar to TCP while maintaining UDP's low-latency characteristics.
These protocols choose UDP over TCP usually because they require low latency, simplicity, or high real-time performance of data transmission, and can tolerate a certain degree of data loss or out-of-order arrival. Note that although UDP provides the required characteristics for these applications, in practice they may also need to implement additional mechanisms at the application layer to ensure data reliability or integrity
Same as TCP, the WB2 SDK also uses the socket interface to control the UDP PCB; you just need to specify the stream type as SOCK_DGRAM when creating the socket
Of course, UDP also requires initialization, connection, sending, receiving, and closing
The official SDK also provides corresponding example programs:
- Connecting to another UDP service as a client (unicast)
- Starting a UDP service and waiting for client connections
- Broadcast
- Multicast
Let me also add the concepts of broadcast and multicast:
The difference between broadcast UDP and unicast UDP is simply the IP address: broadcast uses the broadcast address 255.255.255.255 and sends the message to every host on the same broadcast network. It is worth emphasizing: local broadcast messages are not forwarded by routers. Of course, this is very easy to understand, because if routers forwarded broadcast messages, it would inevitably paralyze the network. This is also why the designers of the IP protocol deliberately did not define an Internet-wide broadcast mechanism.
Broadcast addresses are commonly used, for example, for players on the same local network in online games to exchange status information with each other.
Actually, as the name suggests, broadcasting means speaking to everyone on the LAN, but broadcast still needs to specify the receiver's port number, because it is impossible for all the ports of the receiver to listen to the broadcast.
Multicast, also called "group cast", logically groups hosts of the same service type in the network. When sending and receiving data, the data only travels within the same group; other hosts that have not joined the group cannot send or receive the corresponding data.
When multicasting over a wide area network, switches and routers only replicate and forward data to the hosts that need to obtain the data. A host can request to join or leave a group from the router. Routers and switches in the network selectively replicate and transmit data, delivering it only to the hosts in the group. This multicast capability can send data to multiple hosts at once while ensuring that it does not affect the other communications of hosts that do not need it (have not joined the group).
Compared with traditional one-to-one unicast, multicast has the following advantages:
1. Hosts with the same service join the same data stream and share the same channel, saving bandwidth and server resources. It has the advantages of broadcast without the bandwidth that broadcast requires.
2. The server's total bandwidth is not limited by the client bandwidth. Since the multicast protocol determines whether to forward the data stream based on the receiver's needs, the server-side bandwidth is constant, independent of the number of clients.
3. Like unicast, multicast is allowed to be transmitted over wide area networks, i.e., the Internet, while broadcast can only be performed on the same local area network.
Disadvantages of multicast:
1. Compared with unicast, multicast has no error correction mechanism; when an error occurs it is hard to remedy, but this function can be implemented at the application layer.
2. Multicast network support has defects and requires the support of routers and the network protocol stack.
3. Multicast applications mainly include online video, online conferencing, etc.

