为何TCP客户端停止send()后recv()始终返回-1?
TCP客户端recv()返回错误码11问题分析与解答
核心结论
TCP通信不需要send()与recv()配对执行,二者是完全独立的操作:send()负责向对端发送数据,recv()负责读取对端发来的数据,不存在必须成对调用的约束。你遇到的recv()返回-1(错误码11,对应EAGAIN/EWOULDBLOCK)的问题,和是否调用send()无关,是代码中的逻辑错误以及对socket行为的误解导致的。
具体问题排查与修复
1. 状态切换的语法错误
代码中切换状态时误用了比较运算符==,而非赋值运算符=,导致状态永远无法正确切换:
// 错误写法 state == AWAKE; state == SLEEPING; // 正确写法 state = AWAKE; state = SLEEPING;
2. 字符串缓冲区未正确终止
recv()读取数据后,未在rx_buffer末尾添加字符串终止符\0,会导致strcmp()的行为未定义,可能误判消息内容:
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); if(len > 0){ rx_buffer[len] = '\0'; // 必须添加终止符,确保字符串操作正常 }
3. 错误码11的含义与处理
错误码11对应EAGAIN或EWOULDBLOCK,表示当前socket为非阻塞模式,且没有可读取的数据。处理方式:
- 确认服务器是否确实在持续发送数据,或数据是否已被之前的
recv()读取完毕 - 如果是阻塞socket,该错误通常不会出现,需检查是否代码或框架(如ESP-IDF)默认将socket设置为非阻塞模式
- 非阻塞模式下,可在遇到该错误时短暂休眠后重试,避免空循环占用CPU
4. TCP连接下误用sendto()
TCP是面向连接的协议,connect()成功建立连接后,应使用send()而非sendto()(sendto()主要用于UDP无连接场景):
// 错误写法(TCP连接下冗余) int err_socket = sendto(sock, message, strlen(message), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); // 正确写法 int err_socket = send(sock, message, strlen(message), 0);
修正后的核心逻辑示例
int state = AWAKE; while(1){ if(state == AWAKE){ int err_socket = send(sock, message, strlen(message), 0); if(err_socket < 0){ ESP_LOGE(TCP_TAG, "Send failed: errno %d", errno); } } int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); if(len < 0){ // 区分无数据可读与真正的错误 if(errno == EAGAIN || errno == EWOULDBLOCK){ vTaskDelay(pdMS_TO_TICKS(100)); // 短暂休眠后重试 continue; } ESP_LOGE(TCP_TAG, "Recv error: errno %d", errno); continue; }else if(len == 0){ // len=0表示服务器主动关闭连接 ESP_LOGE(TCP_TAG, "Server closed connection"); break; } rx_buffer[len] = '\0'; // 添加字符串终止符 if(strcmp(rx_buffer, "ON") == 0){ state = AWAKE; }else if(strcmp(rx_buffer, "OFF") == 0){ state = SLEEPING; } }
内容的提问来源于stack exchange,提问作者Fredy Chen
相关产品推荐
相关产品推荐

