网站可能存在什么问题吗,直播app开发要多少钱,个人门户网站开发,常熟网站设计1. 简介 UDP协议#xff08;User Datagram Protocol#xff09;#xff0c;全称用户数据报协议#xff0c;它是一种面向非连接的协议#xff0c;面向非连接指的是在正式通信前不必与对方先建立连接#xff0c; 不管对方状态就直接发送。至于对方是否可以接收到这些数据内…1. 简介 UDP协议User Datagram Protocol全称用户数据报协议它是一种面向非连接的协议面向非连接指的是在正式通信前不必与对方先建立连接 不管对方状态就直接发送。至于对方是否可以接收到这些数据内容UDP协议无法控制因此说UDP协议是一种不可靠的协议。UDP协议适用于一次只传送少量数据、对可靠性要求不高的应用环境。 因为UDP的数据传输不一定是一对一的所以也衍生出单播、组播和广播的概念。
1. 单播 单播unicast是指封包在计算机网络的传输中目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛通常所使用的网络协议或服务大多采用单播传输例如TCP协议。
2. 多播 组播multicast也叫多播或群播。 指把信息同时传递给一组目的地址。它使用策略是最高效的因为消息在每条网络链路上只需传递一次而且只有在链路分叉的时候消息才会被复制。
3. 广播 广播broadcast是指封包在计算机网络中传输时目的地址为网络中所有设备的一种传输方式。
2. lwIP ESP-IDF使用lwIP库实现TCP/IP协议栈这个库在大多数嵌入式系统中都有用到它是一个轻量的TCP/IP协议层套件。 支持以下特性
IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over multiple network interfacesICMP (Internet Control Message Protocol) for network maintenance and debuggingIGMP (Internet Group Management Protocol) for multicast traffic managementMLD (Multicast listener discovery for IPv6). Aims to be compliant with RFC 2710. No support for MLDv2ND (Neighbor discovery and stateless address autoconfiguration for IPv6). Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 (Address autoconfiguration)DHCP, AutoIP/APIPA (Zeroconf) and (stateless) DHCPv6UDP (User Datagram Protocol) including experimental UDP-lite extensionsTCP (Transmission Control Protocol) with congestion control, RTT estimation fast recovery/fast retransmit and sending SACKsraw/native API for enhanced performanceOptional Berkeley-like socket APITLS: optional layered TCP (altcp) for nearly transparent TLS for anyTCP-based protocol (ported to mbedTLS) (see changelog for more info)PPPoS and PPPoE (Point-to-point protocol over Serial/Ethernet)DNS (Domain name resolver incl. mDNS)6LoWPAN (via IEEE 802.15.4, BLE or ZEP) 支持以下应用层协议
HTTP server with SSI and CGI (HTTPS via altcp)SNMPv2c agent with MIB compiler (Simple Network Management Protocol), v3 via altcpSNTP (Simple network time protocol)NetBIOS name service responderMDNS (Multicast DNS) responderiPerf server implementationMQTT client (TLS support via altcp)
3. 例程 例程分别在ESP32上实现TCP客户端和服务端使用电脑作为另一方进行简单通信测试。需要注意的是测试时ESP32和电脑必须处于同一局域网。 电脑端测试使用的上位机为VOFA下载地址VOFA
3.1 发送端 这个例程配置ESP32为发送端当连接WiFi热点成功后会向目标IP每隔1秒发送一段数据。
#include freertos/FreeRTOS.h
#include freertos/queue.h
#include freertos/semphr.h
#include esp_system.h
#include esp_wifi.h
#include esp_event.h
#include esp_log.h
#include esp_mac.h
#include nvs_flash.h
#include sys/socket.h
#include lwip/err.h
#include lwip/sys.h
#include netdb.h
#include arpa/inet.h#include string.h#define TAG app
#define HOST_IP_ADDR 172.16.10.26
#define HOST_PORT 20001static const char *payload Hello from ESP32\r\n;
static TaskHandle_t client_task_handle;static void udp_client_task(void *args)
{struct sockaddr_in dest_addr;dest_addr.sin_addr.s_addr inet_addr(HOST_IP_ADDR);dest_addr.sin_family AF_INET;dest_addr.sin_port htons(HOST_PORT);int sock socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (sock 0) {ESP_LOGE(TAG, Unable to create socket: errno %d, errno);}ESP_LOGI(TAG, Socket created, sending to %s:%d, HOST_IP_ADDR, HOST_PORT);while (1) {int err sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)dest_addr, sizeof(dest_addr));if (err 0) {ESP_LOGE(TAG, Error occurred during sending: errno %d, errno);break;}ESP_LOGI(TAG, Message sent);vTaskDelay(1000 / portTICK_PERIOD_MS);}close(sock);vTaskDelete(NULL);
}static void wifi_event_handler(void* arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if (event_base IP_EVENT) {if (event_id IP_EVENT_STA_GOT_IP) {xTaskCreate(udp_client_task, udp_client, 4096, NULL, 5, client_task_handle);}} else if (event_base WIFI_EVENT) {if (event_id WIFI_EVENT_STA_DISCONNECTED) {vTaskDelete(client_task_handle);} else if (event_id WIFI_EVENT_STA_START) {esp_wifi_connect();}}
}int app_main()
{/* 初始化NVS */esp_err_t ret nvs_flash_init();if (ret ESP_ERR_NVS_NO_FREE_PAGES || ret ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ESP_ERROR_CHECK(nvs_flash_init());}/* 初始化WiFi协议栈 */ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());esp_netif_create_default_wifi_sta();wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(cfg));ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handler,NULL,NULL));ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,ESP_EVENT_ANY_ID,wifi_event_handler,NULL,NULL));wifi_config_t wifi_config {.sta {.ssid QXL,.password 88888888,.threshold.authmode WIFI_AUTH_WPA_WPA2_PSK,},};ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config));ESP_ERROR_CHECK(esp_wifi_start());return 0;
}ESP32的WiFi驱动初始化在前面的文章已经有详细的介绍了这里不再赘述。 因为UDP是面向无连接的所以编程会相对简单流程如下
1. 创建套接字 调用socket函数。第一个参数表示IP协议这里使用IPv4对应IP_INET第二个参数表示socket类型UDP协议只能填SOCK_DGRAM第三个参数表示协议栈类型这里填IPPROTO_IP。函数会返回套接字描述符。
2. 套接字配置可选 socket的接收和发送默认是阻塞的即如果数据不到达就一直等待所以根据应用的需要设置接收的超时时间。 调用setsockopt函数。第一个参数为套接字描述符第二个参数为协议层必须填SOL_SOCKET第三个参数为设置项填SO_RCVTIMEO设置超时时间当然函数还支持以下配置项
#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */
#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */
#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */
#define SO_LINGER 0x0080 /* linger on close if data present */
#define SO_DONTLINGER ((int)(~SO_LINGER))
#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */
#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address port reuse */
#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */
#define SO_RCVBUF 0x1002 /* receive buffer size */
#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */
#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */
#define SO_SNDTIMEO 0x1005 /* send timeout */
#define SO_RCVTIMEO 0x1006 /* receive timeout */
#define SO_ERROR 0x1007 /* get error status and clear */
#define SO_TYPE 0x1008 /* get socket type */
#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */
#define SO_NO_CHECK 0x100a /* dont create UDP checksum */
#define SO_BINDTODEVICE 0x100b /* bind to device */比较常用的就是SO_RCVTIMEO设置接收超时SO_SNDTIMEO设置发送超时。上面带Unimlemented注释的是ESP-IDF不支持的配置。 每种配置项需要传入的数据是不一样的用之前建议参考官方的说明文档。设置超时传入的是struct timeval这个结构体。
struct timeval {time_t tv_sec;suseconds_t tv_usec;
};tv_sec设置秒数tv_usec设置微秒数。
3. 发送数据 调用sendto函数。参数1为套接字描述符参数2为数据指针参数3为数据长度参数4为标志位有以下选项
#define MSG_PEEK 0x01
#define MSG_WAITALL 0x02
#define MSG_OOB 0x04
#define MSG_DONTWAIT 0x08
#define MSG_MORE 0x10
#define MSG_NOSIGNAL 0x20 这些标志位是发送和接收都支持的比较常用的是MSG_DONTWAIT像发送和接收函数是阻塞的使能这个标志位可以让函数立即返回不等待数据。 参数5为目标地址信息结构体如下
struct sockaddr_in {u8_t sin_len;sa_family_t sin_family;in_port_t sin_port;struct in_addr sin_addr;
#define SIN_ZERO_LEN 8char sin_zero[SIN_ZERO_LEN];
};
#endif /* LWIP_IPV4 */sin_len数据长度一般不需要填sin_family套接字类型IPv4填AF_INETIPv6填AF_INET6其他填AF_UNSPECsin_port端口sin_zero预留字节不用管。 参数6为目标地址结构体长度。
4. 关闭连接 调用close函数。传入套接字描述符即可。 使用上位机时数据引擎选择“RawData”数据接口选择UDP远程IP填写ESP32获取到的IP本地端口要与ESP32发送报文的端口一致。设置好后点击左上角的圆形按钮即可启动连接在下面文本框能看到接收到的消息。 2.2 接收端 这个例程创建一个UDP接收端接收所有发送至指定端口的UDP包。
#include freertos/FreeRTOS.h
#include freertos/queue.h
#include freertos/semphr.h
#include esp_system.h
#include esp_wifi.h
#include esp_event.h
#include esp_log.h
#include esp_mac.h
#include nvs_flash.h
#include sys/socket.h
#include lwip/err.h
#include lwip/sys.h
#include netdb.h
#include arpa/inet.h#include string.h#define TAG app
#define SERVER_PORT 20001static TaskHandle_t server_task_handle;static void udp_server_task(void *args)
{char rx_buffer[128];struct sockaddr_in dest_addr;dest_addr.sin_addr.s_addr htonl(IPADDR_ANY);dest_addr.sin_family AF_INET;dest_addr.sin_port htons(SERVER_PORT);int sock socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (sock 0) {ESP_LOGE(TAG, Unable to create socket: errno %d, errno);}int opt 0;setsockopt(sock, SOL_SOCKET, SO_BROADCAST, opt, sizeof(int));int err bind(sock, (struct sockaddr *)dest_addr, sizeof(dest_addr));if (err 0) {ESP_LOGE(TAG, Socket unable to bind: errno %d, errno);goto __exit;}ESP_LOGI(TAG, Socket bound to port %d, SERVER_PORT);while (1) {struct sockaddr_in source_addr {0};socklen_t socklen sizeof(source_addr);memset(rx_buffer, 0, sizeof(rx_buffer));int len recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)source_addr, socklen);if (len 0) {ESP_LOGE(TAG, recvfrom failed: errno %d, errno);} else {ESP_LOGI(TAG, Received %d bytes from IPSTR :%d, data: %s, len, IP2STR((esp_ip4_addr_t *) source_addr.sin_addr), source_addr.sin_port, rx_buffer);}}__exit:close(sock);vTaskDelete(NULL);
}static void wifi_event_handler(void* arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if (event_base IP_EVENT) {if (event_id IP_EVENT_STA_GOT_IP) {xTaskCreate(udp_server_task, udp_server, 4096, NULL, 5, server_task_handle);}} else if (event_base WIFI_EVENT) {if (event_id WIFI_EVENT_STA_DISCONNECTED) {vTaskDelete(server_task_handle);} else if (event_id WIFI_EVENT_STA_START) {esp_wifi_connect();}}
}int app_main()
{/* 初始化NVS */esp_err_t ret nvs_flash_init();if (ret ESP_ERR_NVS_NO_FREE_PAGES || ret ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ESP_ERROR_CHECK(nvs_flash_init());}/* 初始化WiFi协议栈 */ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());esp_netif_create_default_wifi_sta();wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(cfg));ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handler,NULL,NULL));ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,ESP_EVENT_ANY_ID,wifi_event_handler,NULL,NULL));wifi_config_t wifi_config {.sta {.ssid Your SSID,.password Your password,.threshold.authmode WIFI_AUTH_WPA_WPA2_PSK,},};ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config));ESP_ERROR_CHECK(esp_wifi_start());return 0;
}接收者的流程和发送者差不多下面只介绍有差异的部分。
1. 创建套接字 参考前面。
2. 配置套接字可选 调用setsockopt函数。除了前面介绍到的配置UDP还支持一个SO_BROADCAST的配置用来使能广播包的接收我这里是配置为禁用。
3. 绑定IP可选 调用bind函数。参数1为套接字描述符参数2为IP地址结构体参数3为结构体长度。通过bind可以绑定监听的IP地址和端口只有接收到的数据包目标IP和端口一致才会处理我这里配置接收任意IP端口为20001。
4. 接收数据 调用recvfrom函数。传入参数和sendto一致不同的是后面的IP地址结构体是输出参数不需要初始化它会返回数据包的源IP信息。
5. 关闭套接字 参考前面。 上位机的配置和前面基本一样唯一的不同是远程端口要保证和ESP32绑定的端口一致。