问:在tcp通讯时,如果发送缓存区已满,怎么办?
在TCP通信中,如果发送缓冲区已满(`tcp_write`返回`ERR_MEM`),需要合理处理以避免数据丢失或阻塞系统。以下是针对缓冲区满情况的完整解决方案:
---
### **1. 缓冲区满的检测与处理逻辑**
当调用`tcp_write`时返回`ERR_MEM`,说明协议栈的发送缓冲区已满。此时需根据应用场景选择以下策略之一:
#### **方法1:阻塞等待(适合实时性要求低的场景)**
```c
void tcp_send_data(void) {
struct tcp_pcb *pcb = get_tcp_connection();
uint8_t buffer[20];
prepare_sensor_data(buffer);
// 尝试发送,若缓冲区满则循环等待
while (1) {
err_t err = tcp_write(pcb, buffer, sizeof(buffer), TCP_WRITE_FLAG_COPY);
if (err == ERR_OK) {
tcp_output(pcb);
break; // 发送成功,退出循环
}
else if (err == ERR_MEM) {
// 缓冲区满:等待协议栈释放空间(需定期调用MX_LWIP_Process)
MX_LWIP_Process(); // 处理协议栈任务(如ACK接收、缓冲区释放)
HAL_Delay(1); // 短暂延时避免忙等
}
else {
// 其他错误(如连接断开)
break;
}
}
}
```
**注意**:此方法会阻塞主循环,需确保`MX_LWIP_Process()`能被及时调用。
---
#### **方法2:丢弃当前数据(适合高频传感器数据)**
```c
void tcp_send_data(void) {
if (send_flag) {
send_flag = 0;
struct tcp_pcb *pcb = get_tcp_connection();
uint8_t buffer[20];
prepare_sensor_data(buffer);
err_t err = tcp_write(pcb, buffer, sizeof(buffer), TCP_WRITE_FLAG_COPY);
if (err == ERR_OK) {
tcp_output(pcb);
}
// 若缓冲区满(ERR_MEM),直接丢弃本次数据(不重试)
// 可根据需要记录丢包统计
}
}
```
**适用场景**:数据更新频率远高于发送频率(如传感器数据),允许偶尔丢包。
---
#### **方法3:缓存数据后重试(平衡可靠性与实时性)**
```c
#define MAX_PENDING_DATA 10 // 最大缓存数据包数
uint8_t pending_buf[MAX_PENDING_DATA][20];
uint8_t pending_count = 0;
void tcp_send_data(void) {
struct tcp_pcb *pcb = get_tcp_connection();
uint8_t buffer[20];
prepare_sensor_data(buffer);
// 优先发送缓存的数据
while (pending_count > 0) {
err_t err = tcp_write(pcb, pending_buf[0], 20, TCP_WRITE_FLAG_COPY);
if (err == ERR_OK) {
tcp_output(pcb);
// 移除已发送的数据
memmove(pending_buf[0], pending_buf[1], (pending_count-1)*20);
pending_count--;
} else {
break; // 缓冲区仍满,退出循环
}
}
// 尝试发送新数据
if (pending_count == 0) {
err_t err = tcp_write(pcb, buffer, 20, TCP_WRITE_FLAG_COPY);
if (err == ERR_MEM) {
// 缓冲区满且无缓存:保存新数据
if (pending_count < MAX_PENDING_DATA) {
memcpy(pending_buf[pending_count], buffer, 20);
pending_count++;
}
} else if (err == ERR_OK) {
tcp_output(pcb);
}
}
}
```
**优势**:平衡实时性与可靠性,避免频繁丢包。
---
### **2. 关键问题与优化建议**
#### **2.1 如何减少缓冲区满的概率?**
- **增大发送缓冲区**:修改`lwipopts.h`中的配置:
```c
#define TCP_SND_BUF 8192 // 默认4KB,可调整为8KB或更大
```
- **降低单次数据量**:拆分大数据包为多小包发送。
- **提高协议栈处理频率**:确保`MX_LWIP_Process()`调用间隔≤1ms。
#### **2.2 如何动态调整发送频率?**
若频繁遇到`ERR_MEM`,可动态延长发送周期(如从20ms调整为30ms):
```c
void adjust_send_interval(void) {
static uint32_t last_adjust_time = 0;
if (HAL_GetTick() - last_adjust_time > 1000) { // 每1秒检查一次
if (tcp_send_fail_count > 5) { // 失败次数阈值
current_interval = MIN(100, current_interval + 10); // 逐步增加间隔
} else {
current_interval = MAX(20, current_interval - 5); // 恢复原始间隔
}
__HAL_TIM_SET_AUTORELOAD(&htim2, current_interval * 10 - 1); // 调整定时器
last_adjust_time = HAL_GetTick();
}
}
```
#### **2.3 监控缓冲区状态**
通过`tcp_sndbuf()`获取当前可用发送缓冲区大小,预判是否可发送:
```c
if (tcp_sndbuf(pcb) >= sizeof(buffer)) {
tcp_write(pcb, buffer, sizeof(buffer), TCP_WRITE_FLAG_COPY);
}
```
---
### **3. 总结**
| **方法** | **适用场景** | **缺点** |
|------------------------|----------------------------------|------------------------------|
| 阻塞等待 | 必须保证数据不丢失 | 可能阻塞系统 |
| 丢弃当前数据 | 高频非关键数据(如传感器) | 丢包风险 |
| 缓存数据后重试 | 平衡可靠性与实时性 | 增加内存开销 |
**推荐组合方案**:
1. **默认使用缓存重试**(方法3),设置合理的缓存队列长度。
2. **动态监控网络状态**:通过`tcp_sndbuf()`和错误统计调整发送频率。
3. **关键数据优先发送**:在缓存队列中实现优先级策略。