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
相关产品推荐
相关产品推荐

