/* linux-5.10.x\net\ipv4\tcp.c
* 从接收缓冲区中读取数据,并将其拷贝到用户提供的缓冲区中。该函数还负责处理一些特殊情况,如接收紧急数据和查看发送队列中的数据
1、从接收缓冲区中读取数据,并将其拷贝到用户提供的缓冲区中
2、处理接收到的FIN标志,表示连接的一端已关闭
3、处理MSG_PEEK标志,如果设置了该标志,则不会从接收缓冲区中删除数据
4、处理接收时间戳,如果设置了相应的控制消息标志,则会设置接收时间戳
5、处理接收队列长度控制消息,如果设置了相应的控制消息标志,则会获取接收队列长度的提示,并设置控制消息
*/
int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
int flags, int *addr_len)
{
struct tcp_sock *tp = tcp_sk(sk); //将TCP套接字的指针转为tcp_sock类型的指针,以便访问TCP套接字特定的成员变量
int copied = 0; //用于记录拷贝的字节数
u32 peek_seq; //用于记录MSG_PEEK的序列号
u32 *seq; //用于指向数据包的序列号
unsigned long used; //用于记录已使用的缓冲区大小
int err, inq; //用于记录错误码和入队标志
int target; /* Read at least this many bytes 表示至少需要读取的字节数*/
long timeo; //表示接收超时时间
struct sk_buff *skb, *last; //用于在数据包链表中遍历
u32 urg_hole = 0; //表示紧急数据的空隙
struct scm_timestamping_internal tss; //用于存储时间戳信息
int cmsg_flags; //表示控制消息的标志位
/* unlikely()宏不会改变程序逻辑,只用于提示编译器某个条件的发生概率较低,让编译器更加关注对其他分支的优化,从而提高代码执行的效率 */
if (unlikely(flags & MSG_ERRQUEUE)) //有错误队列消息
return inet_recv_error(sk, msg, len, addr_len); //处理接收错误队列,并返回结果
if (sk_can_busy_loop(sk) && skb_queue_empty_lockless(&sk->sk_receive_queue) &&
(sk->sk_state == TCP_ESTABLISHED)) //套接字类型支持忙等待、接收队列为空、TCP套接字处于已建立状态
sk_busy_loop(sk, nonblock); //在非阻塞模式下进行忙等待
lock_sock(sk); //锁定套接字,以保证在执行后续操作时的线程安全
err = -ENOTCONN; //将错误码设置为未连接
if (sk->sk_state == TCP_LISTEN) //如果套接字状态为监听状态
goto out; //则跳转到out标签,表示连接未建立
cmsg_flags = tp->recvmsg_inq ? 1 : 0; //根据TCP套接字接收消息入队标志,设置控制消息的标志位
timeo = sock_rcvtimeo(sk, nonblock); //获取接收超时时间
/* Urgent data needs to be handled specially. */
if (flags & MSG_OOB) //如果有应急数据
goto recv_urg; //则跳转到recv_urg标签,表示接收紧急数据
if (unlikely(tp->repair)) {
//如果TCP套接字处于损坏修复状态
err = -EPERM; //将错误码设置为权限不足
if (!(flags & MSG_PEEK)) //如果flags参数中不包含MSG_PEEK标志位
goto out; //则跳转到out标签,表示权限错误
if (tp->repair_queue == TCP_SEND_QUEUE) //如果修复队列为发送队列
goto recv_sndq; //则跳转到处理发送队列
err = -EINVAL; //将错误码设置为无效参数
linux内核代码-注释详解:tcp_recvmsg
最新推荐文章于 2024-11-18 23:04:59 发布