函数原型
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数
-
sockfd:
这是一个已打开的套接字描述符,它标识了要从其接收数据的网络连接。对于 TCP,这个套接字通常是通过socket 函数创建的,并且已经通过connect 函数与远程服务器建立了连接,或者通过accept 函数接受了来自客户端的连接。 -
buf:
这是一个指向缓冲区的指针,该缓冲区用于存储从套接字接收到的数据。在调用recv 函数之前,应确保该缓冲区有足够的空间来存储要接收的数据。 -
len:
这是缓冲区buf 的长度,以字节为单位。它指定了缓冲区中可以存储的最大数据量。 -
flags:
这是一个整数值,用于传递特殊的接收标志给底层协议。这些标志可以修改recv 函数的行为。通常,这个参数被设置为 0,表示使用标准的接收行为。然而,一些可能的标志包括:MSG_PEEK :查看数据,但不从套接字的接收队列中移除。MSG_WAITALL :等待直到接收到完整的len 字节数据,或者发生错误。然而,这个标志的行为可能因实现而异,并且不建议在阻塞模式下使用。MSG_OOB :仅接收带外数据(out-of-band data)。MSG_DONTWAIT :非阻塞模式操作(等效于使用非阻塞套接字)。MSG_ERRQUEUE :获取扩展的错误信息(较少使用)。
返回值
- 如果成功,
recv 函数返回实际接收到的字节数。这个数字可能小于len 参数指定的长度,这取决于发送方发送的数据量、套接字的接收缓冲区中的数据量以及网络条件。 - 如果连接已正常关闭,
recv 函数返回 0。 - 如果出现错误,
recv 函数返回 -1,并设置全局变量errno 以指示错误类型。
错误处理
当
EWOULDBLOCK 或EAGAIN :套接字是非阻塞的,并且没有数据可供立即接收。ECONNRESET :连接被对端重置。ENOTCONN :套接字未连接到远程地址。EINTR :接收操作被中断,通常是因为接收到了一个信号。EBADF :提供的套接字描述符不是有效的或不支持接收操作。
注意事项
-
阻塞与非阻塞:根据套接字的配置,
recv 函数可以表现为阻塞或非阻塞。在阻塞模式下,recv 会等待直到有数据可以接收或发生错误。在非阻塞模式下,如果没有数据可供接收,recv 会立即返回EWOULDBLOCK 或EAGAIN 错误。 -
多次接收:由于 TCP 的流性质,一次
recv 调用可能不会接收到发送方发送的所有数据。因此,可能需要多次调用recv 来接收完整的消息或数据流。 -
数据边界:
recv 函数不保证按发送方发送的原始边界接收数据。应用程序需要自己处理消息的边界问题,通常通过在消息前加上长度字段或使用特定的分隔符。 -
关闭连接:当对端关闭连接时,
recv 函数将返回 0,表示没有更多的数据可以接收。这是正常关闭连接的指示。 -
性能考虑:与
send 类似,频繁地接收小块数据可能不如一次性接收大块数据高效。应用程序应优化数据传输以提高性能。
在使用
示例代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> int main() { // 假定 sockfd 是已经连接好的套接字 int sockfd = /* socket(...) */; // 用于存储接收数据的缓冲区 char buffer[1024]; // 清空缓冲区 memset(buffer, 0, sizeof(buffer)); // 接收数据 int bytes_received = recv(sockfd, buffer, sizeof(buffer), 0); if (bytes_received < 0) { // 处理错误 perror("recv failed"); } else if (bytes_received == 0) { // 对方已经关闭了连接 printf("Peer has performed an orderly shutdown "); } else { // 打印接收到的数据 printf("Received (%d bytes): %.*s ", bytes_received, bytes_received, buffer); } // 关闭套接字 close(sockfd); return 0; }
在上面的代码示例中,`sockfd` 应已经是一个成功连接的 socket—这意味着在 TCP 的情况下,应该在客户端使用
在实际应用程序中,通常会将
void perror(const char *str);
其中
例如,如果你调用了一个可能失败的函数(如
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> int main() { int fd = open("nonexistent_file.txt", O_RDONLY); if (fd == -1) { perror("Error opening file"); return EXIT_FAILURE; } // ... 其他代码 ... return EXIT_SUCCESS; }
如果文件
Error opening file: No such file or directory
这里,“Error opening file” 是传递给