socket recv函数系列,recv,recvfrom,recvmsg,一文彻底弄懂,可作参考手册

目录

1.系统调用流程

2.接收函数系列

2.1 recv函数

2.2 recvfrom函数

2.3 recvmsg函数

2.4 read函数

2.5 readv函数


1.系统调用流程

图 1

2.接收函数系列

2.1 recv函数

套接字接收函数。

 
  1. #include <sys/types.h>

  2. #include <sys/socket.h>

  3. ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  4. 参数:

  5. sockfd:套接字文件描述符

  6. buf:接收缓冲区

  7. len:接收数据长度

  8. flags:标识

  9. 返回值:

  10. 成功:返回接收的字节数

  11. 失败:返回-1,并设置errno


2.2 recvfrom函数

套接字接收函数,能够获取对端套接字地址信息。

 
  1. #include <sys/types.h>

  2. #include <sys/socket.h>

  3. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

  4. struct sockaddr *src_addr, socklen_t *addrlen);

  5. 参数:

  6. sockfd:套接字文件描述符

  7. buf:接收缓冲区

  8. len:接收数据长度

  9. flags:标识

  10. src_addr:对端套接字地址

  11. addrlen:对端套接字地址长度

  12. 返回值:

  13. 成功:返回接收的字节数

  14. 失败:返回-1,并设置errno


2.3 recvmsg函数

套接字批量接收函数。

 
  1. #include <sys/types.h>

  2. #include <sys/socket.h>

  3. ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

  4. 参数:

  5. sockfd:套接字文件描述符

  6. msg:接收消息体

  7. flags:标识

  8. 返回值:

  9. 成功:返回接收的字节数

  10. 失败:返回-1,并设置errno

图 2 struct msghdr结构体解析

 
recvmsg接收原理:

  • void *msg_name保存对端套接字地址,socklen_t msg_namelen为套接字地址长度。
  • struct iovec *msg_iov为接收单元结构体指针,指向一块连续的struct iovec区域,每一个区域为一个接收缓冲区,可以通过size_t msg_iovlen指定一次接收的单元数量,从而完成批量接收操作。
  • struct iovec为接收单元,void *iov_base指向一块内存缓冲区,unsigned int iov_len为缓冲区长度,接收数据会先填满一个内存缓冲区才会继续填写下一个缓冲区,如图3所示。

图 3

 
使用示例:

 
  1. #define PACK_NUM (50)

  2. #define MAX_BUF_LEN (1500)

  3. struct sockaddr_in peer = {0};

  4. socklen_t addrlen = sizeof(peer);

  5. char *buf_arr = (char *)malloc(PACK_NUM * MAX_BUF_LEN);

  6. struct iovec *iov = (struct iovec *)malloc(PACK_NUM * sizeof(struct iovec));

  7. for (int i = 0; i < PACK_NUM; i++) {

  8. iov[i].iov_base = &buf_arr[i * MAX_BUF_LEN];

  9. memset(iov[i].iov_base, 0, MAX_BUF_LEN);

  10. iov[i].iov_len = strlen(REQUEST_STR1);

  11. }

  12. struct msghdr msg = {0};

  13. msg.msg_name = &peer;

  14. msg.msg_namelen = sizeof(peer);

  15. msg.msg_iov = iov;

  16. msg.msg_iovlen = 10;

  17. ssize_t len = recvmsg(sockfd, &msg, 0);


2.4 read函数

文件描述符接收函数,可用于套接字接收。

 
  1. #include <unistd.h>

  2. ssize_t read(int fd, void *buf, size_t count);

  3. 参数:

  4. fd:套接字文件描述符

  5. buf:接收缓冲区

  6. count:接收数据长度

  7. 返回值:

  8. 成功:返回接收的字节数

  9. 失败:返回-1,并设置errno


2.5 readv函数

文件描述符接收函数,可用于套接字批量接收操作接收。

 
  1. #include <sys/uio.h>

  2. ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

  3. 参数:

  4. fd:套接字文件描述符

  5. iov:接收单元结构体指针

  6. iovcnt:接收单元数量

  7. 返回值:

  8. 成功:返回接收的字节数

  9. 失败:返回-1,并设置errno


readv接收原理:

  • struct iovec为接收单元,void *iov_base指向一块内存缓冲区,unsigned int iov_len为缓冲区长度。
  • int iovcnt为接收单元数量。
  • readv函数和recvmsg函数接收过程类似。


使用示例:

 
  1. #define PACK_NUM (50)

  2. #define MAX_BUF_LEN (1500)

  3. char *buf_arr = (char *)malloc(PACK_NUM * MAX_BUF_LEN);

  4. struct iovec *iov = (struct iovec *)malloc(PACK_NUM * sizeof(struct iovec));

  5. for (int i = 0; i < PACK_NUM; i++) {

  6. iov[i].iov_base = &buf_arr[i * MAX_BUF_LEN];

  7. memset(iov[i].iov_base, 0, MAX_BUF_LEN);

  8. iov[i].iov_len = strlen(REQUEST_STR1);

  9. }

  10. readv(sockfd, iov, 10);

socket recv函数系列,recv,recvfrom,recvmsg,一文彻底弄懂,可作参考手册-优快云博客

<think>我们正在讨论LWIP中的lwip_recv函数是否为阻塞调用。根据引用[1]和[3]的信息,我们可以得出结论: 在LWIP的Socket API中,`lwip_recv()`函数默认是阻塞的(blocking)。这意味着当调用`lwip_recv()`时,如果没有数据可读,当前任务会进入阻塞状态,直到有数据到达或者发生错误。 具体分析如下: 1. **阻塞行为**: - 引用[1]指出,在服务器端,`recv()`(以及`recvfrom()`, `recvmsg()`)是用于接收数据的函数。在默认情况下,这些函数会阻塞当前线程,直到有数据可读。 - 引用[3]详细说明了非阻塞模式下`recv`函数的返回值。其中提到,在非阻塞模式下,当没有数据可读时,`recv`会立即返回一个小于0的值(具体为-1),并且错误码为`EWOULDBLOCK`或`EAGAIN`。这反过来也说明,在默认(阻塞)模式下,如果没有数据可读,`recv`会一直等待。 2. **函数实现**: - 引用[2]显示,`lwip_recv`函数实际上是调用`lwip_recvfrom`实现的: ```c int lwip_recv(int s, void *mem, size_t len, int flags) { return lwip_recvfrom(s, mem, len, flags, NULL, NULL); } ``` 因此,`lwip_recv`的行为与`lwip_recvfrom`一致。 3. **非阻塞模式设置**: - 要改变默认的阻塞行为,可以将套接字设置为非阻塞模式。这通常通过`fcntl`函数设置`O_NONBLOCK`标志来实现,或者使用`ioctlsocket`(在LWIP中可能是`lwip_ioctl`)设置非阻塞模式。 - 在非阻塞模式下,当没有数据可读时,`lwip_recv`会立即返回错误(返回值-1),并将错误码设置为`EWOULDBLOCK`。 4. **超时设置**: - 引用[4]提供了一种设置接收超时的方法,使用`setsockopt`设置`SO_RCVTIMEO`选项。这样,即使是在阻塞模式下,如果在指定的时间内没有数据到达,`recv`函数也会返回(返回值为0表示超时?注意:超时返回时,返回值可能是-1,错误码为`EAGAIN`或`EWOULDBLOCK`,具体实现可能有所不同,但通常超时返回0表示对端关闭连接,而超时没有数据返回-1且错误码为超时相关)。 总结: - **默认行为**:`lwip_recv`是阻塞调用,如果没有数据可读,调用任务会一直阻塞。 - **非阻塞模式**:可以通过设置套接字为非阻塞模式,使得`lwip_recv`在没有数据时立即返回。 - **超时设置**:也可以通过设置接收超时,让阻塞调用在指定时间后返回。 因此,回答用户的问题:是的,在默认情况下,`lwip_recv`是阻塞的。 如果用户需要非阻塞行为,可以按照以下方式设置套接字为非阻塞模式(示例代码): ```c #include <fcntl.h> int flags = lwip_fcntl(sock, F_GETFL, 0); lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK); ``` 或者设置接收超时: ```c struct timeval tv; tv.tv_sec = 5; // 5秒超时 tv.tv_usec = 0; lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); ``` 注意:在FreeRTOS中,阻塞调用会导致当前任务挂起,直到条件满足,这期间不会占用CPU资源,因此合理使用阻塞调用是可以接受的。但是,如果希望同时处理多个连接,则需要考虑使用非阻塞模式或多任务。 §§相关问题§§ 1. 如何将LWIP套接字设置为非阻塞模式? 2. 在LWIP中如何处理多个阻塞套接字? 3. LWIP中`recv`函数的超时设置有哪些注意事项?</think>在 LWIP SOCK 编程中,**`lwip_recv()` 函数默认是阻塞调用**。以下是关键分析: ### 阻塞行为详解 1. **默认阻塞模式** - 当调用 `lwip_recv()` 时,如果接收缓冲区中没有数据,任务会**主动挂起**(进入阻塞状态),直到: - 数据到达接收缓冲区 - 连接被对方关闭(返回 `0`) - 发生错误(返回负值) - 这是由 LWIP 的 `netconn` 层实现决定的[^1][^2] 2. **阻塞机制实现** ```c int lwip_recv(int s, void *mem, size_t len, int flags) { return lwip_recvfrom(s, mem, len, flags, NULL, NULL); // 最终调用netconn_recv() } ``` 在 `netconn_recv()` 中,当无数据时会调用 `sys_arch_mbox_fetch()` 挂起任务,等待网络事件唤醒[^2]。 3. **返回值含义**(阻塞模式下)[^3]: | 返回值 | 含义 | |--------|--------------------------| | > 0 | 成功接收到的字节数 | | = 0 | 对方关闭连接 | | < 0 | 错误(需检查 `errno`) | --- ### 如何改为非阻塞模式 通过设置套接字选项可实现非阻塞接收: ```c // 设置非阻塞模式 int flags = lwip_fcntl(sock, F_GETFL, 0); lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK); // 非阻塞调用示例 int len = lwip_recv(sock, buf, sizeof(buf), 0); if (len < 0) { if (errno == EWOULDBLOCK) { // 无数据时的特殊错误码 // 可在此处理其他任务 } else { // 真实错误处理 } } ``` ### 超时控制方法 即使保持阻塞模式,也可设置接收超时[^4]: ```c struct timeval tv; tv.tv_sec = 5; // 5秒超时 tv.tv_usec = 0; lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); // 阻塞调用但最多等待5秒 int len = lwip_recv(sock, buf, sizeof(buf), 0); if (len < 0 && errno == EWOULDBLOCK) { // 超时处理 } ``` --- ### 阻塞行为的底层原理 LWIP 通过邮箱(mailbox)实现任务同步: 1. 当调用 `lwip_recv()` 时,若接收队列为空: - 任务被挂起到与该套接字关联的邮箱 - 任务从就绪列表中移除 2. 当数据包到达时: - 网络中断服务程序将数据放入接收队列 - 通过 `sys_mbox_post()` 唤醒等待任务 3. 被唤醒的任务继续执行接收操作 > 在 FreeRTOS 中,这种机制依赖 `sys_arch_mbox_fetch()` 实现,其内部使用 `xQueueReceive()` 或信号量[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值