美文网首页ESP8266 Arduino物联网开发之旅
ESP8266开发之旅 网络篇⑩ UDP服务

ESP8266开发之旅 网络篇⑩ UDP服务

作者: 单片机菜鸟博哥 | 来源:发表于2019-06-06 10:18 被阅读3次

    1.1 前言

        前面的博文中,博主讲述的内容基本上都是Tcp以及Http通信的内容,那么我们当然得讲解一下Tcp的另外一个兄弟——UDP。

    1.1.1 TCP与UDP优缺点

    1. TCP是面向连接,也就是发送数据之前是需要建立连接;UDP是面向无连接的,即发送数据之前不需要建立连接。
    2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力做到可靠,即不保证绝对可靠。
    3. UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
    4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
    5. TCP对系统资源要求较多,UDP对系统资源要求较少。

    UDP 是 User Datagram Protocol 的简称,是一种无连接、不可靠的协议,每一个数据报都是一个独立的信息,它在网络上以任何可能的路径传到目的地,但不保证是否真的传到目的地、是否过程中真的保证了数据的完整性!

    UDP就好像无手机时代,你要去探望亲戚,但是你不知道亲戚有没有在家(也就是说可能会丢包);

    TCP就好像有手机时代,你要去探望亲戚,你会打电话过去提前沟通好,你会确保亲戚在家里才会买东西过去探望(数据不会丢包);

    使用UDP服务,请在代码前加入一下头文件:

    #include <WiFiUdp.h>
    

    1.2 WiFiUDP库

        老规矩,先上一个博主总结的百度脑图:


    image

    总体上,根据功能可以把方法分为4大类:

    • 启动UDP服务方法;
    • 处理发送过来的UDP包;
    • 获取UDP client的信息;
    • 发送UDP包;

    1.2.1 启动UDP服务方法

    1.2.1.1 begin —— 开启UDP服务

    函数说明:

    /**
     * 初始化TCP服务,开始监听特定端口
     * @return  1 表示成功
     *          0 表示没有有效的socket可以用
     */
    uint8_t begin(uint16_t port);   
    

    1.2.1.2 stop —— 停止UDP服务

    函数说明:

    /**
     * 断开UDP连接
     */
    void stop();    
    

    1.2.2 处理发送过来的UDP包

    1.2.2.1 parsePacket —— 解析UDP数据包

    函数说明:

    /**
     * 开始处理进来的UDP请求
     * @return  int 返回当前UDP包的大小,如果没有就返回0
     */
    int parsePacket();  
    

    注意点:

    • 此方法要在 available、read、peek之前调用(总结来说,就是读取数据之前应该解析数据);

    1.2.2.2 available —— 判断UDP数据包可读大小

    函数说明:

    /**
     * 返回当前udp包的可读剩余字节数据
     */
    int available();    
    

    1.2.2.3 read —— 读取数据并清除

    函数说明:

    /**
     * 读取当前udp数据包的一个字节,并从缓冲区清除该字节
     * @return 单字节数据
     */
    int read(); 
    
    /**
     * 读取当前udp数据包的len长度的数据,并从缓冲区清除该字节
     * @param buffer 存储数据的内存空间
     * @param len 需要读取的长度
     * @return 读取成功字节数
     */
    int read(unsigned char* buffer, size_t len);
    

    注意点:

    • 此方法读取完数据之后会把数据从缓冲区去掉;

    1.2.2.4 peek —— 读取数据

    函数说明:

    /**
     * 读取当前udp数据包的一个字节
     * @return 单字节数据
     */
    int peek(); 
    

    注意点:

    • 此方法读取完数据之后会不会把数据从缓冲区去掉;
    • 建议尽量使用批量处理函数;

    1.2.2.5 flush —— 清除缓冲区

    函数说明:

    /**
     * 清除缓冲区
     */
    void flush();   
    

    1.2.3 获取UDP 远端的信息

    1.2.3.1 remoteIP —— 远端IP地址

    函数说明:

    /**
     * 获取发送当前UDP包的主机IP地址
     * @return IPAddress ip地址
     */
    IPAddress remoteIP();   
    

    1.2.3.2 remotePort —— 远端端口

    函数说明:

    /**
     * 获取发送当前UDP包的主机端口
     * @return uint16_t 端口
     */
    uint16_t remotePort();  
    

    1.2.4 发送UDP包

    1.2.4.1 beginPacket —— 配置ip和port

    函数说明:

    /**
     * 开始创建需要发送给远端主机的udp包
     * @param ip 远端主机ip地址
     * @param port 远端主机端口号
     * @return 1 创建成功
     *         0 创建失败
     */
    int beginPacket(IPAddress ip, uint16_t port);
    int beginPacket(const char *host, uint16_t port);
    

    1.2.4.2 write —— 把数据写入发送缓冲区

    函数说明:

    /**
     * 写入一字节数据到创建好的udp包
     * @param uint8_t 单字节数据
     * @return 1 写入成功
     *         0 写入失败
     */
    size_t write(uint8_t);
    
    /**
     * 写入字节数据到创建好的udp包
     * @param buffer 字节数据缓冲区
     * @return size_t 返回写入成功的字节数
     */ 
    size_t write(const uint8_t *buffer);
    
    /**
     * 写入size字节数据到创建好的udp包
     * @param buffer 字节数据缓冲区
     * @param size 字节数据长度
     * @return size_t 返回写入成功的字节数
     */ 
    size_t write(const uint8_t *buffer, size_t size);
    

    1.2.4.3 endPacket —— 发送数据

    函数说明:

    /**
     * 把udp数据包发送出去
     * @param uint8_t 单字节数据
     * @return 1 发送成功
     *         0 发送失败
     */
    int endPacket();
    

    1.3 实例

    1.3.1 通过UDP收发数据

    实例说明

    • ESP8266作为UDP服务端,把电脑UDP客户端发过来的数据打印到串口调试器,并回复应答信息;

    源码:

    #include <ESP8266WiFi.h>
    #include <WiFiUdp.h>
    
    const char* ssid = "********";//wifi账号
    const char* password = "********";//wifi密码
    
    WiFiUDP Udp;
    unsigned int localUdpPort = 4210;  // 本地监听端口
    char incomingPacket[255];  // 存储Udp客户端发过来的数据
    char  replyPacket[] = "Hi there! Got the message :-)";  // 应答信息
    
    
    void setup()
    {
      Serial.begin(115200);
      Serial.println();
    
      Serial.printf("Connecting to %s ", ssid);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED)
      {
        delay(500);
        Serial.print(".");
      }
      Serial.println(" connected");
    
      //启动Udp监听服务
      Udp.begin(localUdpPort);
      //打印本地ip地址,udp client端会使用到
      Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
    }
    
    
    void loop()
    {
      //解析Udp数据包
      int packetSize = Udp.parsePacket();
      if (packetSize)
      {
        // 收到Udp数据包
        Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
        // 读取Udp数据包
        int len = Udp.read(incomingPacket, 255);
        if (len > 0)
        {
          incomingPacket[len] = 0;
        }
        //向串口调试器打印信息
        Serial.printf("UDP packet contents: %s\n", incomingPacket);
    
        //往udp 远端发送应答信息
        Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
        Udp.write(replyPacket);
        Udp.endPacket();
      }
    }
    

    注意点:

    • 为了模拟UDP客户端请求,请下载使用以下软件 Packet Sender

    测试结果:

    • ESP8266先开启UDP服务,然后Packet Sender发送UDP数据:
    image image

    1.3.2 通过UDP控制LED

    实例说明

    • ESP8266作为UDP服务端,把电脑UDP客户端发过来的命令用来控制板载LED灯,其中 LED_ON 表示开灯,LED_OFF表示关灯,操作完毕后需要回复信息;

    源码:

    #include <ESP8266WiFi.h>
    #include <WiFiUdp.h>
    
    const char *ssid = "********";     //wifi名称
    const char *password = "********"; //wifi密码
    
    WiFiUDP Udp;
    unsigned int localUdpPort = 4210; // 本地端口号
    char incomingPacket[255];         // 接收缓冲区
    
    void setup()
    {
      //以下为基本功能初始化,初始化串口和网络和LED
      pinMode(LED_BUILTIN, OUTPUT);
      Serial.begin(115200);
      Serial.println();
      Serial.printf("Connecting to %s ", ssid);
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED)
      {
        delay(500);
        Serial.print(".");
      }
      Serial.println(" connected");
    
      //以下开启UDP监听并打印输出信息
      Udp.begin(localUdpPort);
      Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
    }
    
    void loop()
    {
      int packetSize = Udp.parsePacket(); //获取当前队首数据包长度
      if (packetSize)  // 有数据可用
      {
        Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
        int len = Udp.read(incomingPacket, 255); // 读取数据到incomingPacket
        if (len > 0)                             // 如果正确读取
        {
          incomingPacket[len] = 0; //末尾补0结束字符串
          Serial.printf("UDP packet contents: %s\n", incomingPacket);
    
          if (strcmp(incomingPacket, "LED_OFF") == 0) // 命令LED_OFF
          {
            digitalWrite(LED_BUILTIN, HIGH); // 熄灭LED
            sendCallBack("LED has been turn off");
          }
          else if (strcmp(incomingPacket, "LED_ON") == 0) // 如果收到LED_ON
          {
            digitalWrite(LED_BUILTIN, LOW); // 点亮LED
            sendCallBack("LED has been turn on");
          }
          else // 如果非指定消息
          {
            sendCallBack("Command Error!");
          }
        }
      }
    }
    
    /**
     * 发送响应信息
     */
    void sendCallBack(const char *buffer){
       Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
       Udp.write(buffer); //回复内容
       Udp.endPacket(); 
    }
    
    

    测试结果:

    • ESP8266先开启UDP服务,然后Packet Sender发送UDP命令:


      image
    image

    1.4 总结

    使用Arduino for esp8266可以非常简单实现UDP通讯过程。最重要需要记住的一点,那就是UDP面向无连接,发送出去的数据不会理会是否被收到。

    学习群 绝对不辜负你的期待 491507716 博主私人号 2421818708

    相关文章

      网友评论

        本文标题:ESP8266开发之旅 网络篇⑩ UDP服务

        本文链接:https://www.haomeiwen.com/subject/ladyxctx.html