ESP32S3 使用IPv6 与服务端通信
手动构建IPv6请求发送的过程
在低成本的环境中使用IPv6
低成本设备(如Arduino、ESP32等)通常并不直接支持IPv6通信,因此在这些设备上实现IPv6请求,需要手动构建协议栈,模拟发送和接收HTTP请求。
- 硬件限制与挑战:
- 在低成本硬件上,内存和处理能力较为有限,可能无法直接使用像
WiFiClient
那样的高级库。 - 为了处理IPv6请求,需要手动编写底层的socket通信,管理内存,并自行构建HTTP报文。
- 在低成本硬件上,内存和处理能力较为有限,可能无法直接使用像
原生的Arduino库不支持IPv6请求的发送
- 问题描述:原生的Arduino网络库(如
WiFi
、Ethernet
)不支持IPv6协议,这使得在Arduino平台上发送IPv6请求成为一个挑战。 - 解决方案:
- 使用底层的Socket编程来实现IPv6通信。通过C语言的
socket
、connect
、send
、recv
等函数手动控制通信过程。 - 使用
inet6_aton()
函数来转换IPv6地址,确保网络通信的正确性。
- 使用底层的Socket编程来实现IPv6通信。通过C语言的
请求发送过程(手动构建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);
-
步骤
:
- 创建一个IPv6 Socket(使用
socket()
函数)。 - 使用
inet6_aton()
将IPv6地址转化为网络字节序。 - 通过
connect()
建立与服务器的连接。 - 构建一个完整的HTTP POST请求,设置请求头(如
Host
、Content-Type
、Content-Length
等)。 - 发送构建好的HTTP请求报文。
- 创建一个IPv6 Socket(使用
请求回复的接收
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);
-
步骤
:
- 使用
recv()
函数接收数据(响应)。 - 判断是否出现接收超时、信号中断等情况,确保程序能够稳定运行。
- 将接收到的数据存入
data
缓冲区。 - 最终关闭socket连接。
- 使用
踩过的坑
请求内容太大导致内存溢出
- 问题描述:由于设备内存有限,发送大文件或长数据时,可能会导致内存溢出,导致程序崩溃或设备重启。
- 解决方案:考虑将请求内容分割成较小的数据包分批发送,避免一次性发送过大数据。发送数据后及时清理内存。
接收数据时只接收到了请求头
- 问题描述:有时会遇到只接收到HTTP响应的头部,而没有得到响应的正文部分。
- 解决方案:
- 检查输出是否完整。
- 检查
recv()
返回值的正确性,确保接收到完整的数据。 - 考虑接收数据时使用
EWOULDBLOCK
和EAGAIN
错误进行适当处理,确保每次接收的数据都不会丢失。