recvfrom()

recvfrom()
简述:
  接收一个数据报并保存源地址。

  #include <winsock.h>

  int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags,
  struct sockaddr FAR* from, int FAR* fromlen);

  s:标识一个已连接套接口的描述字。
  buf:接收数据缓冲区。
  len:缓冲区长度。
  flags:调用操作方式。
  from:(可选)指针,指向装有源地址的缓冲区。
  fromlen:(可选)指针,指向from缓冲区长度值。

注释:
  本函数由于从(已连接)套接口上接收数据,并捕获数据发送源的地址。
  对于SOCK_STREAM类型的套接口,最多可接收缓冲区大小个数据。如果套接口被设置为线内接收带外数据(选项为SO_OOBINLINE),且有带外数据未读入,则返回带外数据。应用程序可通过调用ioctlsocket()的SOCATMARK命令来确定是否有带外数据待读入。对于SOCK_STREAM类型套接口,忽略from和fromlen参数。
  对于数据报类套接口,队列中第一个数据报中的数据被解包,但最多不超过缓冲区的大小。如果数据报大于缓冲区,那么缓冲区中只有数据报的前面部分,其他的数据都丢失了,并且recvfrom()函数返回WSAEMSGSIZE错误。
  若from非零,且套接口为SOCK_DGRAM类型,则发送数据源的地址被复制到相应的sockaddr结构中。fromlen所指向的值初始化时为这个结构的大小,当调用返回时按实际地址所占的空间进行修改。
  如果没有数据待读,那么除非是非阻塞模式,不然的话套接口将一直等待数据的到来,此时将返回SOCKET_ERROR错误,错误代码是WSAEWOULDBLOCK。用select()或WSAAsynSelect()可以获知何时数据到达。
  如果套接口为SOCK_STREAM类型,并且远端“优雅”地中止了连接,那么recvfrom()一个数据也不读取,立即返回。如果立即被强制中止,那么recv()将以WSAECONNRESET错误失败返回。
  在套接口的所设选项之上,还可用标志位flag来影响函数的执行方式。也就是说,本函数的语义既取决于套接口选项,也取决于标志位参数。标志位可取下列值:
  值 意义
  MSG_PEEK 查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除。
  MSG_OOB 处理带外数据(参见2.2.3节具体讨论)。
  
返回值:
  若无错误发生,recvfrom()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。


错误代码:
  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
  WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
  WSAEFAULT:fromlen参数非法;from缓冲区大小无法装入端地址。
  WSAEINTR:阻塞进程被WSACancelBlockingCall()取消。
  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
  WSAEINVAL:套接口未用bind()进行捆绑。
  WSAENOTCONN:套接口未连接(仅适用于SOCK_STREAM类型)。
  WSAENOTSOCK:描述字不是一个套接口。
  WSAEOPNOTSUPP:指定了MSG_OOB,但套接口不是SOCK_STREAM类型的。
  WSAESHUTDOWN:套接口已被关闭。当一个套接口以0或2的how参数调用shutdown()关闭后,无法再用recv()接收数据。
  WSAEWOULDBLOCK:套接口标识为非阻塞模式,但接收操作会产生阻塞。
  WSAEMSGSIZE:数据报太大无法全部装入缓冲区,故被剪切。
  WSAECONNABORTED:由于超时或其他原因,虚电路失效。
  WSAECONNRESET:远端强制中止了虚电路。

参见:
  recv(), send(), socket(), WSAAsyncSelect().
<think>我们正在讨论recvfrom函数的使用方法。根据提供的引用,recvfrom函数主要用于从套接字接收数据,并捕获发送方的地址。它通常用于无连接的协议(如UDP),但也可以用于已连接的套接字(如SOCK_STREAM类型的TCP套接字,尽管在TCP中我们通常使用recv)。 引用[1]给出了函数定义:int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); 参数说明: s: 套接字描述符 buf: 指向接收缓冲区的指针,用于存放接收到的数据 len: 接收缓冲区的大小 flags: 通常设为0,也可以是一些标志的组合(如MSG_DONTWAIT等) from: 指向struct sockaddr的指针,用于存放发送方的地址信息 fromlen: 指向一个整数的指针,开始时指定from缓冲区的大小,调用后会被设置为实际地址的长度 引用[2]和[3]提到,recvfrom用于从(已连接)套接口上接收数据,并捕获数据发送源的地址。特别指出,对于SOCK_STREAM类型的套接口(即TCP),最多可接收缓冲区大小个数据。 引用[4]强调了sendto()recvfrom()在UDP中的使用,因为UDP是无连接的,所以每次发送和接收都需要指定对方的地址。 引用[5]提到了UDP recvfrom函数与TCP C/S模型的不同。 因此,我们总结recvfrom函数的典型使用场景是UDP套接字,因为UDP每次接收数据时都需要知道数据来自哪里。 下面我们给出一个C语言中使用recvfrom的简单示例(UDP服务器端): */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define BUFLEN 1024 #define PORT 8888 int main() { int sockfd; struct sockaddr_in server_addr, client_addr; char buffer[BUFLEN]; socklen_t addr_len = sizeof(client_addr); ssize_t recv_len; // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } // 配置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); // 绑定套接字 if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } printf("UDP server listening on port %d...\n", PORT); // 接收数据 recv_len = recvfrom(sockfd, buffer, BUFLEN, 0, (struct sockaddr *)&client_addr, &addr_len); if (recv_len < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); } buffer[recv_len] = '\0'; // 确保字符串结束 printf("Received packet from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); printf("Data: %s\n", buffer); close(sockfd); return 0; } /* 说明: 1. 创建UDP套接字:socket(AF_INET, SOCK_DGRAM, 0) 2. 绑定到指定的端口(服务器端) 3. 调用recvfrom接收数据,该函数会阻塞直到有数据到达。 4. recvfrom的返回值是接收到的字节数,如果出错则返回-1。 5. 参数from和fromlen会返回发送方的地址信息,这样服务器就可以知道数据来自哪个客户端,并可以回复。 在Python中,recvfrom的使用类似: import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('0.0.0.0', 8888)) data, addr = sock.recvfrom(1024) # 1024是缓冲区大小 print("Received message:", data, "from", addr) 注意:在TCP中,我们通常使用recv,因为TCP是面向连接的,不需要每次接收都获取地址(连接已经知道对方地址)。但是,recvfrom也可以用于TCP套接字(引用[2]提到),只不过此时from参数会被忽略(设为NULL)或者得到的是对端的地址(但通常TCP连接已经知道对端地址,所以不常用)。 因此,使用recvfrom时,需要根据套接字类型(UDP还是TCP)来决定是否使用from参数。 常见错误: - 忘记绑定(服务器端)或连接(客户端)。 - 缓冲区大小不足,导致数据被截断(UDP不会分片,所以接收的数据包超过缓冲区大小时,多余部分会被丢弃)。 - 没有正确处理返回的地址长度(fromlen参数是值-结果参数,调用前需要设置为from缓冲区的大小,调用后会被修改为实际地址的长度)。 总结:recvfrom主要用于UDP套接字,用于接收数据并获取发送方的地址。 相关问题: 1. recvfrom函数在调用时如果缓冲区不够大,会发生什么? 2. 在UDP通信中,如何利用recvfrom和sendto实现一个简单的回声服务器? 3. recvfrom函数可以用于TCP套接字吗?如果可以,有什么限制? 4. 在使用recvfrom时,如何设置超时以避免永久阻塞? 5. recvfrom函数返回的错误码有哪些?分别代表什么含义? */ </think>### recvfrom函数详解 `recvfrom()`是网络编程中的核心函数,主要用于**从套接字接收数据并获取发送方地址**(尤其在UDP通信中)。以下是详细说明: #### 函数原型(C语言) ```c int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ``` 1. **参数说明** - `sockfd`:套接字描述符(UDP/TCP均可) - `buf`:接收数据的缓冲区 - `len`:缓冲区最大长度 - `flags`:控制标志(通常设为0) - `src_addr`:发送方地址结构体指针(输出参数) - `addrlen`:地址结构体长度(输入输出参数) 2. **返回值** - 成功:接收到的字节数 - 失败:-1(可通过`errno`获取错误码) - 连接关闭:0(TCP场景) --- #### 核心特性 1. **UDP通信** - 主要用途:获取客户端地址(因UDP无连接) - 示例流程: ```c struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); char buffer[1024]; int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &addr_len); ``` 2. **TCP通信** - 可用但非必需(连接已建立,地址已知) - 等效于`recv()`:若`src_addr`设为`NULL`,功能与`recv()`相同[^2][^4] 3. **阻塞行为** - 默认阻塞直到数据到达(可通过`fcntl()`设为非阻塞模式) --- #### Python实现(UDP服务器示例) ```python import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(('0.0.0.0', 8888)) while True: data, client_addr = server_socket.recvfrom(1024) # 关键调用 print(f"Received {len(data)} bytes from {client_addr}") server_socket.sendto(data.upper(), client_addr) # 回复客户端 ``` --- #### 关键注意事项 1. **缓冲区溢出** - 需确保`len` >= 实际数据包大小(否则UDP数据被截断) 2. **地址重用** - UDP服务器应绑定`INADDR_ANY`(`0.0.0.0`)[^3] 3. **TCP限制** - 仅用于已连接套接字,`src_addr`返回对端地址(通常用`recv()`更高效)[^2][^4] > ⚠️ **典型错误**:未初始化`addr_len` > ```c > socklen_t addr_len = sizeof(struct sockaddr_in); // 必须显式赋值 > ``` --- ### 相关问题 1. `recvfrom()`在TCP和UDP中使用有何本质区别?[^2][^4] 2. 如何实现`recvfrom()`的超时机制?(提示:`setsockopt()` + `SO_RCVTIMEO`) 3. 为什么UDP通信中`sendto()`/`recvfrom()`必须指定地址,而TCP不需要?[^3][^5] 4. `recvfrom()`返回`EAGAIN`错误时应如何处理? 5. 如何通过`recvfrom()`实现多播(UDP multicast)数据接收? [^1]: UNIX网络编程标准接口 [^2]: 支持TCP套接字但地址信息冗余 [^3]: UDP须显式管理地址 [^4]: 无连接协议必需源地址 [^5]: TCP连接隐含地址信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值