ESP32S3 使用IPv6 与服务端通信

智能硬件 创建于:06-14 00:24

ESP32S3 使用IPv6 与服务端通信

手动构建IPv6请求发送的过程

在低成本的环境中使用IPv6

低成本设备(如Arduino、ESP32等)通常并不直接支持IPv6通信,因此在这些设备上实现IPv6请求,需要手动构建协议栈,模拟发送和接收HTTP请求。

  • 硬件限制与挑战:
    • 在低成本硬件上,内存和处理能力较为有限,可能无法直接使用像WiFiClient那样的高级库。
    • 为了处理IPv6请求,需要手动编写底层的socket通信,管理内存,并自行构建HTTP报文。

原生的Arduino库不支持IPv6请求的发送

  • 问题描述:原生的Arduino网络库(如WiFiEthernet)不支持IPv6协议,这使得在Arduino平台上发送IPv6请求成为一个挑战。
  • 解决方案
    • 使用底层的Socket编程来实现IPv6通信。通过C语言的socketconnectsendrecv等函数手动控制通信过程。
    • 使用inet6_aton()函数来转换IPv6地址,确保网络通信的正确性。

请求发送过程(手动构建HTTP请求报文)

c++复制代码  struct sockaddr_in6 serverAddr; // 用于存储IPv6地址
  int sock;

  // 创建IPv6 socket
  sock = socket(AF_INET6, SOCK_STREAM, 0);
  if (sock < 0)
  {
    Serial.println("无法创建 socket");
    return "Error";
  }

  // 设置服务器的IPv6地址
  memset(&serverAddr, 0, sizeof(serverAddr));
  serverAddr.sin6_family = AF_INET6;
  serverAddr.sin6_port = htons(port);
  inet6_aton(server, &serverAddr.sin6_addr);

  // 连接到服务器
  if (connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
  {
    Serial.println("连接到IPv6服务器失败");
    close(sock);
    return "Error";
  }

  // 构建HTTP请求
  String httpRequest = String("POST ") + path + " HTTP/1.1\r\n";
  httpRequest += "Host: [" + String(server) + "]\r\n"; // 使用IPv6地址作为Host
  httpRequest += "Content-Type: application/json\r\n";
  httpRequest += "Content-Length: " + String(strlen(postData)) + "\r\n";
  httpRequest += "\r\n";   // 请求头和正文之间的空行
  httpRequest += postData; // POST请求数据
  free(postData);

  // 发送HTTP请求
  send(sock, httpRequest.c_str(), httpRequest.length(), 0);
  • 步骤

    1. 创建一个IPv6 Socket(使用socket()函数)。
    2. 使用inet6_aton()将IPv6地址转化为网络字节序。
    3. 通过connect()建立与服务器的连接。
    4. 构建一个完整的HTTP POST请求,设置请求头(如HostContent-TypeContent-Length等)。
    5. 发送构建好的HTTP请求报文。

请求回复的接收

c++复制代码  // 接收服务器响应
  char buffer[512];
  char data[512] = {0};
  int size_recv;
  while (1)
  {
    memset(buffer, 0, 512); // 清空变量
    if ((size_recv = recv(sock, buffer, sizeof(buffer) - 1, 0)) == -1)
    {
      if (errno == EWOULDBLOCK || errno == EAGAIN)
      {
        printf("recv timeout ...\n");
        break;
      }
      else if (errno == EINTR)
      {
        printf("interrupt by signal...\n");
        continue;
      }
      else if (errno == ENOENT)
      {
        printf("recv RST segment...\n");
        break;
      }
      else
      {
        printf("unknown error!\n");
        exit(1);
      }
    }
    else if (size_recv == 0)
    {
      break;
    }
    else
    {
      strcat(data, buffer);
    }
  }

  // 关闭socket
  close(sock);
  • 步骤

    1. 使用recv()函数接收数据(响应)。
    2. 判断是否出现接收超时、信号中断等情况,确保程序能够稳定运行。
    3. 将接收到的数据存入data缓冲区。
    4. 最终关闭socket连接。

踩过的坑

请求内容太大导致内存溢出

  • 问题描述:由于设备内存有限,发送大文件或长数据时,可能会导致内存溢出,导致程序崩溃或设备重启。
  • 解决方案:考虑将请求内容分割成较小的数据包分批发送,避免一次性发送过大数据。发送数据后及时清理内存。

接收数据时只接收到了请求头

  • 问题描述:有时会遇到只接收到HTTP响应的头部,而没有得到响应的正文部分。
  • 解决方案:
    • 检查输出是否完整。
    • 检查recv()返回值的正确性,确保接收到完整的数据。
    • 考虑接收数据时使用EWOULDBLOCKEAGAIN错误进行适当处理,确保每次接收的数据都不会丢失。

原文地址:https://my.oschina.net/u/8205955/blog/16667362

免责声明:本文来源于互联网,版权归合法拥有者所有,如有侵权请公众号联系管理员

* 本站提供的一些文章、资料是供学习研究之用,如用于商业用途,请购买正版。

日常记录