tcp_recvmsg函数主要功能是从sk->sk_receive_queue队列中获取tcp数据,如果队列中没有数据,则直接等待,否则从sk->sk_receive_queue队列中获取指定长度的数据,然后将此数据拷贝到用户空间中,接着此数据从sk->sk_receive_queue队列中删除。
tcp数据接收过程第二步,tcp_v4_rcv函数代码流程图,如图1-3所示。
图1-3:函数tcp_v4_rcv代码流程图
网卡驱动收到数据后,会触发NET_RX_SOFTIRQ软中断,软中断处理函数net_rx_action通过调用网卡驱动注册的poll函数来进行收包处理,最终通过netif_receive_skb->ip_rcv->ip_rcv_finish->dst_input->ip_local_deliver->ip_local_deliver_finish->tcp_v4_rcv来进行tcp收包处理。tcp_v4_do_rcv首先根据目标ip与端口查找已经建立连接的sock,如果以前没有建立连接则在listen端口中查找,找到以后首先,将tcp头中的相关信息填入skb私有数据区cb中。然后调用tcp_v4_do_rcv进行真正的处理。由于此时sk->sk_state状态是TCP_ESTABLISHED,因此进入tcp_v4_do_rcv流程,它根据sk_state会继续调用tcp_rcv_established函数。tcp_rcv_established是tcp数据接收的核心处理函数。如果收包序列连续则进行快速收包流程。快速收包流程,首先根据ack将待重传数据从重传队列中删除,更新tp->snd_una,然后将数据放入sk->sk_receive_queue队列中,调用tcp_data_ready通知应用层程序收包,最后给对端发送ack。慢速收包处理流程,需要处理tcp数据丢失,乱序重传等场景。它需要根据接收数据的ack,标记哪些数据已经丢失,哪些数据已经确认,哪些数据已经乱序收到,然后根据这些进行拥塞控制,设置拥塞窗口。最后将接收的数据放入sk->sk_receive_queue队列中,调用tcp_data_ready通知应用层程序收包,最后给对端发送ack。