TCP是什么?不需要!使用ESP32网络层直接通信!

文章总结(帮你们节约时间)

  • TCP虽然可靠但带来额外开销,ESP32能够绕过传输层,在网络层甚至链路层直接实现自定义通信协议,显著降低延迟并提高效率。
  • 使用Arduino环境为ESP32实现自定义网络协议不需要深厚的网络知识,通过esp_wifi原生API和lwip库即可实现原始套接字通信和802.11帧发送。
  • 自定义协议特别适合对延迟敏感、资源受限或需要高效率的场景,如实时控制系统、传感器网络和低功耗应用。
  • 虽然舍弃了TCP的可靠性,但通过自定义确认机制、校验和和简单加密,仍可构建稳定且安全的通信系统。

引言

想象一下你正在开派对,而TCP就像那个超级讲究礼节的宾客——先问"你好吗?“,等待"我很好,你呢?”,再回应"我也很好,谢谢",然后才开始真正的对话。天哪!这也太墨迹了吧!有时候,我们就想直接冲进去大喊:“派对开始啦!”,然后开始疯狂蹦迪——这就是我们今天要探讨的:抛开繁文缛节,让ESP32直接在网络层肆意狂欢!谁说通信必须按部就班?老规矩有时候就是用来打破的!

TCP:互联网的礼仪大师

TCP究竟是什么?

传输控制协议(TCP),互联网的脊梁,负责确保数据包从A点到B点的可靠传输。它就像那个神经质的朋友,寄包裹时非要买保险、要签收、要追踪号码,还要打电话确认你收到了没有。是的,非常可靠,但有时候——老实说——也是非常烦人!

TCP建立连接时的"三次握手"堪称数字世界最著名的礼仪:

  1. “嘿,我想跟你聊天!”(SYN)
  2. “好啊,我听到了,也想跟你聊!”(SYN+ACK)
  3. “太好了!我确认听到你也想聊,现在开始吧!”(ACK)

结束通话还要"四次挥手"——比分手还复杂!这一切都是为了确保可靠性,但在某些场景下,这种可靠性就像派对上的监护人——必要但扫兴。

TCP的优缺点

优点:

  • 可靠传输:丢包?重传!乱序?重排!
  • 流量控制:防止发送方淹没接收方
  • 拥塞控制:体贴地照顾整个网络的感受

缺点:

  • 协议开销大:每个数据包都带着厚重的头信息
  • 延迟较高:握手、确认、等待重传都需要时间
  • 资源消耗:对小型嵌入式设备来说可能是奢侈品

ESP32:口袋里的网络猛兽

ESP32不只是一个微控制器,它是一头伪装成芯片的野兽!双核处理器、WiFi、蓝牙、一大堆GPIO,还有各种外设——所有这些竟然塞进了指甲盖大小的封装里!难怪它成为IoT项目的宠儿。

ESP32的网络能力

ESP32内置的WiFi栈支持:

  • 标准TCP/IP协议栈
  • 2.4GHz WiFi(802.11 b/g/n)
  • 软AP模式与Station模式
  • 可同时作为AP和Station运行
  • 最高150Mbps的数据传输率

但更令人兴奋的是,ESP32允许我们抛开传统,深入底层网络协议,自行设计通信方式。这就像有了厨房的钥匙,不再局限于点菜单上的食物,而是可以按自己的喜好创造美食!

网络分层:探索TCP之下的世界

在深入ESP32的直接网络层通信前,让我们理解网络的分层结构。传统的OSI模型将网络分为7层,而TCP/IP简化模型分为4层:

  1. 应用层:HTTP、FTP、SMTP等
  2. 传输层:TCP、UDP
  3. 网络层:IP
  4. 链路层:以太网、WiFi等

TCP位于传输层,而我们今天的冒险将跳过它,直接在网络层甚至链路层上构建通信。这就像绕过高速公路的收费站,走那些鲜为人知的小路——可能崎岖但兴奋刺激!

为什么要绕过TCP?

你可能会问:"TCP工作得很好,为什么要另起炉灶?"好问题!以下是一些compelling的理由:

  • 极低延迟:没有握手、确认等礼节,数据即发即至
  • 减少开销:更小的包头意味着更高的有效数据比例
  • 资源节约:对内存和处理能力有限的设备尤为重要
  • 自定义控制:完全掌控通信流程,按需调整
  • 特殊应用:某些实时应用如游戏控制器、传感器网络等不需要TCP的可靠性而更看重速度

ESP32直接网络层通信:原理详解

理解原始套接字(Raw Sockets)

原始套接字允许程序直接访问底层网络协议,绕过传输层。这就像拥有VIP通道,可以跳过安检直接登机!在ESP32上,我们可以创建原始套接字来发送和接收IP数据包,甚至是以太网帧。

自定义协议设计

设计自己的协议时,需要考虑几个关键因素:

  1. 帧格式:定义数据包的结构,包括头部字段和载荷
  2. 寻址方案:如何标识和定位网络中的设备
  3. 错误检测:如何发现传输错误(如校验和)
  4. 数据分片与重组:大数据如何分割和重建
  5. 流控制:如何防止接收方被淹没

我们的简单协议可能看起来像这样:

[魔数(2字节)][源地址(4字节)][目标地址(4字节)][数据长度(2字节)][数据(变长)][校验和(4字节)]

魔数可以是0xE5P3,帮助接收方识别这是我们的协议包而非其他随机数据。

在Arduino中实现ESP32网络层直接通信

让我们用Arduino IDE为ESP32创建一个简单的直接网络层通信示例。首先是发送端:

#include <WiFi.h>
#include <lwip/ip.h>
#include <lwip/raw.h>

// WiFi凭据
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";

// 自定义协议ID(魔数)
const uint16_t PROTOCOL_MAGIC = 0xE5P3;

// 目标设备的IP地址
IPAddress targetIP(192, 168, 1, 100);

// 原始套接字
struct raw_pcb* raw_pcb;

// 自定义数据包结构
typedef struct {
   
   
  uint16_t magic;        // 协议魔数
  uint32_t sourceIP;     // 源IP
  uint32_t destIP;       // 目标IP
  uint16_t dataLength;   // 数据长度
  uint8_t data[256];     // 数据负载
  uint32_t checksum;     // 校验和
} CustomPacket;

// 计算校验和
uint32_t calculateChecksum(uint8_t* data, size_t length) {
   
   
  uint32_t sum = 0;
  for (size_t i = 0; i < length; i++) {
   
   
    sum += data[i];
  }
  return sum;
}

// 准备自定义数据包
void preparePacket(CustomPacket* packet, const char* message) {
   
   
  packet->magic = PROTOCOL_MAGIC;
  packet->sourceIP = WiFi.localIP();
  packet->destIP = (uint32_t)targetIP;
  
  size_t messageLength = strlen(message);
  packet->dataLength = messageLength;
  memcpy(packet->data, message, messageLength);
  
  // 计算除校验和外的所有字段的校验和
  packet->checksum = calculateChecksum((uint8_t*)packet, 
                                      sizeof(CustomPacket) - sizeof(uint32_t));
}

// 发送回调函数
u8_t send_callback(struct raw_pcb* pcb, struct pbuf* p, const ip_addr_t* addr) {
   
   
  // 可以在这里添加发送状态处理代码
  return 0;
}

void setup() {
   
   
  Serial.begin(115200);
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
   
   
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi连接成功");
  Serial.print("IP地址: ");
  Serial.println(WiFi.localIP());
  
  // 创建原始套接字
  ip_addr_t anyAddr;
  IP4_ADDR(&anyAddr, 0, 0, 0, 0);
  
  // 使用IP协议255表示自定义协议
  raw_pcb = raw_new(255);
  
  if (raw_pcb != NULL) {
   
   
    raw_recv(raw_pcb, send_callback, NULL);
    raw_bind(raw_pcb, &anyAddr);
    Serial.println("原始套接字创建成功");
  } else {
   
   
    Serial.println("原始套接字创建失败");
  }
}

void loop() {
   
   
  // 每5秒发送一条消息
  static unsigned long lastSendTime = 0;
  unsigned long currentTime = millis();
  
  if (currentTime - lastSendTime > 5000) {
   
   
    lastSendTime = currentTime;
    
    // 准备数据包
    CustomPacket packet;
    char message[100];
    sprintf(message, "来自ESP32的直接网络层消息! 时间: %lu", currentTime);
    preparePacket(&packet, message);
    
    // 创建pbuf并填充数据
    struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, sizeof
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值