ESP32学习day4之UDP通信

网络通信原理
这里只是简单的了解一下,勉强够用就行。
TCP/IP协议
OSI模型共有七层,前面的物理层我应该已经用到了,就是前面调以太网的phy。TCP/IP协议中,自底而上因用法被简化为4个层次,数据链路层、网络层、传输层和应用层。

数据链路层实现网卡接口的网络驱动程序,以处理数据在物理媒介。数据链路层两个常用的协议是ARP,RARP。
网络层实现数据包的选路和转发。WAN通常使用众多分级的路由器来连接分散的主机或LAN,因此,通信的两台主机一般不是直接相连的,使得在传输层和网络应用程序来看,通信的双方是直接相连的。
网络层最核心的协议是IP协议。IP协议根据数据包的目的IP地址来决定如何投递它。如果数据包不能直接发送给目标主机,那么IP协议根据数据包的目的IP地址来决定如何投递它。如果数据包不能直接发送给目标主机,那么IP协议就为它寻找一个合适的下一跳路由器,并将数据包交付给该路由器来转发。逐跳(hop by hop)
传输层,两个协议,TCP/UDP协议,这次我用的是UDP协议,目的端通过数据校验发现数据错误而将其丢弃,UDP只是单独通知应用程序发送失败。使用UDO协议的应用程序通常要自己处理数据确认、超时重传等逻辑。程序每次发送数据都要明确指定接收端的地址(IP地址等信息)。基于数据报的服务,区别于数据流服务,每个UDP数据报都一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出。

**网络套接字**
socket,封装了TCP/IP协议的通信编程接口,套接字,实际上是一个通信端点。需要主机IP和端口号才能建立连接。

UDP套接字编程模型

socket() : 创建套接字。
bind() : 绑定IP:Port
connect() :将套接字连接到目的地址。
listen() : 监听
accept() : 接收连接请求。
send()/recv() 和 sendto()/recvfrom() : 发送和接收数据。
closesocket() : 关闭套接字

-------------------------------------------分割线--------------------------------------------

网络通讯常识和逻辑过程。
任何一个socket通讯,都需要IP地址和port端口号的。不指定局域网内的某一设备,局域网所有设备如果监听了这个端口号,那么都可以收到esp32的消息。
如果要指定的IP地址的设备,那么就需要指定明确的地址。

-------------------------------------------分割线--------------------------------------------

UDP通信,UDP函数参考:
uint8_t begin(IPAddress a,uint16_t p)
函数功能:启动监听来自与于某个地址发送给某个端口的数据或监听某个端口的数据参数
a 为监听的IP地址;p为监听的端口号

int parsePacket()
函数功能:获取接收数据信息
返回值:如果有数据包可用,则返回队首数据包长度,否则返回0

IPAddress remoteIP()
函数功能:获取目标设备的IP地址
返回值:目标IP地址

uint16_t remotePort()
函数功能:获取目标设备的端口号
返回值:目标端口号

int read(char* buffer, size_t len)
函数功能:读取数据

int beginPacket(IPAddress ip, uint16_t port)
函数功能:准备发送数据
参数
ip为目标IP
port为目标端口号

int endPacket()
函数功能:发送数据

void stop()
函数功能:停止监听,释放资源

------------------------------------------分割线---------------------------------------------

第一步:上电后连接路由器,获取路由器分配的IP地址
第二步:系统消息监听,如果收到IP地址成功获取的回调,则开始创建socket
第三步:涉及到要连接服务器,是否存在?所以先判断下是否存在,比较全面,虽然说UDP是不可靠的,但是这样做,可以避免许多事情!或者连接成功路由器之后直接发送到指定的地址,不管是否存在。
第四步:一旦服务器有响应,我这里就发送一个字符串到服务器。

`/* BSD Socket API Example

This example code is in the Public Domain (or CC0 licensed, at your option.)

Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/

include <string.h>

include <sys/param.h>

include "freertos/FreeRTOS.h"

include "freertos/task.h"

include "esp_system.h"

include "esp_wifi.h"

include "esp_event.h"

include "esp_log.h"

include "nvs_flash.h"

include "esp_netif.h"

include "protocol_examples_common.h"

include "lwip/err.h"

include "lwip/sockets.h"

include "lwip/sys.h"

include <lwip/netdb.h>

define PORT 3333

static const char *TAG = "example";

void udp_server_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
struct sockaddr_in6 dest_addr;

while (1) {

    if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    } else if (addr_family == AF_INET6) {
        bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }

    int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Socket created");

if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)

    if (addr_family == AF_INET6) {
        // Note that by default IPV6 binds to both protocols, it is must be disabled
        // if both protocols used at the same time (used in CI)
        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
    }

endif

    int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err < 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    while (1) {

        ESP_LOGI(TAG, "Waiting for data");
        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        socklen_t socklen = sizeof(source_addr);
        int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

        // Error occurred during receiving
        if (len < 0) {
            ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
            break;
        }
        // Data received
        else {
            // Get the sender's ip address as string
            if (source_addr.sin6_family == PF_INET) {
                inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
            } else if (source_addr.sin6_family == PF_INET6) {
                inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
            }

            rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
            ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
            ESP_LOGI(TAG, "%s", rx_buffer);

            int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr));
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                close(sock);
                break;
            }
        }
    }

    if (sock != -1) {
        ESP_LOGE(TAG, "Shutting down socket and restarting...");
        shutdown(sock, 0);
        close(sock);
    }
}
vTaskDelete(NULL);

}
`
这是udp的官方例程,我的收发现在没有回应

我人晕了。。。。

不知道是哪里的问题,只能从头开发分析,为了不烦躁和耐得下性子,我又开始边工作边写博客。
现在我打算引入监控端口的函数,让他显示在monitor上
东搞西搞就搞出来了。。。。。。

posted @ 2021-10-26 22:08  肆月黄妙之  阅读(1795)  评论(0编辑  收藏  举报