You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

send()与recv()返回值是什么?如何实现已知长度消息完整收发?

解决Socket中send()/recv()无法全量收发的问题

我来帮你捋清楚这个问题——TCP是流式协议,没有天然的消息边界,所以send()和recv()的返回值并不是你指定的长度,而是实际完成传输的字节数,这也是你只收到部分数据的原因。

先搞懂send()和recv()的返回值含义

  • 返回值 > 0:成功发送/接收的字节数,这个数值可能小于你传入的len参数。原因可能是系统发送缓冲区满了,或者网络传输时被拆分成了多个数据包,所以得循环处理直到完成全部数据的传输。
  • 返回值 = 0:对端已经主动关闭了连接(recv()时更常见),这时候没有更多数据可以接收了。
  • 返回值 < 0:调用出错,需要检查errno判断具体问题:
    • EINTR:调用被信号中断,可以重试;
    • EAGAIN/EWOULDBLOCK:非阻塞模式下当前没有数据/缓冲区满,需要等待后重试;
    • ECONNRESET:连接被对端重置,需要处理连接断开的情况。

实现全量收发的封装函数

既然知道了问题所在,我们可以封装两个函数,循环调用send()/recv()直到完成全部数据的传输:

全量接收函数:full_recv()

#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

ssize_t full_recv(int sockfd, void *buf, size_t len) {
    size_t total_received = 0;
    ssize_t bytes_received;
    char *buf_ptr = (char *)buf;

    while (total_received < len) {
        bytes_received = recv(sockfd, buf_ptr, len - total_received, 0);
        
        if (bytes_received == -1) {
            // 被信号中断则重试,其他错误直接返回-1
            if (errno == EINTR) {
                continue;
            }
            perror("recv failed");
            return -1;
        } else if (bytes_received == 0) {
            // 对端关闭连接,返回已接收的字节数
            fprintf(stderr, "Peer closed connection\n");
            return total_received;
        }

        total_received += bytes_received;
        buf_ptr += bytes_received;
    }

    return total_received;
}

全量发送函数:full_send()

ssize_t full_send(int sockfd, const void *buf, size_t len) {
    size_t total_sent = 0;
    ssize_t bytes_sent;
    const char *buf_ptr = (const char *)buf;

    while (total_sent < len) {
        bytes_sent = send(sockfd, buf_ptr, len - total_sent, 0);
        
        if (bytes_sent == -1) {
            if (errno == EINTR) {
                continue;
            }
            perror("send failed");
            return -1;
        } else if (bytes_sent == 0) {
            fprintf(stderr, "Peer closed connection\n");
            return total_sent;
        }

        total_sent += bytes_sent;
        buf_ptr += bytes_sent;
    }

    return total_sent;
}

用法示例

比如你要发送"hello world"(长度11字节):

const char *msg = "hello world";
size_t msg_len = strlen(msg);

if (full_send(sockfd, msg, msg_len) != msg_len) {
    // 处理发送失败或未全量发送的情况
    fprintf(stderr, "Failed to send full message\n");
}

接收时,如果你已经知道消息长度是11字节:

char buffer[12]; // 留一个字节存字符串终止符
if (full_recv(sockfd, buffer, 11) == 11) {
    buffer[11] = '\0'; // 转成可打印的字符串
    printf("Received: %s\n", buffer);
} else {
    // 处理接收失败或连接断开的情况
    fprintf(stderr, "Failed to receive full message\n");
}

额外注意事项

  • 如果你不确定消息长度,通常的做法是先发送消息长度,再发送消息内容:比如先发送一个4字节的整数表示消息长度,接收方先收这个长度,再收对应字节数的消息。
  • 如果使用非阻塞Socket,需要结合select()/poll()/epoll()来等待数据就绪后再调用recv()/send(),否则会频繁遇到EAGAIN错误。

内容的提问来源于stack exchange,提问作者ryanmattscott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:30:05