tcp的三次握手主要是通过__sys_connect来进行的,而__sys_connect函数流程分析又分为客户端与服务器端,其客户端代码流程如图1-1所示。
图1-1:函数__sys_connect代码流程图
1.__sys_connect函数首先根据fd查找对应的file,接着将用户空间的连接地址拷贝到内核空间,然后调用__sys_connect_file。
2.__sys_connect_file首先根据file找到sock,然后调用sock->ops->connect函数,对tcp来说即inet_stream_ops:inet_stream_connect。
3.sock_from_file根据file查找sock。
4.__inet_stream_connect函数的包装函数。
5.__inet_stream_connect首先根据sockàstate状态(此时为SS_UNCONNECTED)来调用sk->sk_prot->connect,接着根据sk的阻塞标志来等待连接完成或者直接返回失败,并设置sock->state状态为SS_CONNECTING。
6.Tcp连接的主要工作函数,对tcp来说即tcp_prot:tcp_v4_connect,它主要完成tcp syn包的封装,路由查找,然后数据发送,其代码流程如图1-2所示。
7.等待三次握手成功函数。
图1-2:函数tcp_v4_connect代码流程图
8. tcp_v4_connect首先根据源目的地址调用ip_route_connect查找路由rt,并调用sk_setup_caps将rt->dst保存到sk->sk_dst_cache中,设置sk->sk_state状态为TCP_SYN_SENT,然后调用inet_hash_connect将sk hash到hashinfo->ehash链表中,最后调用tcp_connect发起真正的tcp syn请求。
9. ip_route_connect路由查找函数,返回rt(struct rtable *rt)指针,它里面有一个重要的成员函数rt->dst-> output查找路由时会赋值为ip_output,真正发送数据时会调用。其详细流程在后续路由模块中再来解析。
10. inet_hash_connect函数主要功能是将要发送syn请求的sk(struct sock *sk)散列到tcp_hashinfo. Ehash链表中,需要注意的是计算散列值时,是以源ip,源端口,目标ip,目标端口来生成的,这样当收到对端的请求,只需以目标ip,目标端口,源ip,源端口来生成hash值查找建立请求的sk即可。
11. tcp_connect真正的建连函数,它首先调用tcp_connect_init初使化tcp syn请求的相关参数,比如mss,初使序列号,windows等,接着分配skb,设置tp->tcp_mstamp(此字段是收发包时记录时间)时间,设置tp->retrans_stamp(第一个重传包的时间)时间,接着调用tcp_rbtree_insert将其插入sk->tcp_rtx_queue(收到ack时才会中此树中删除)树中,最后调用tcp_transmit_skb真正地发送syn请求。
12. tcp_connect_init主要作用时进行tcp syn消息相关字段的初使化。
13. tcp数据的管理是以buff(struct sk_buff *buff)为单位的,此函数是分配struct sk_buff数据结构。
14. 红黑树插入函数,此函数是将buff插入到sk->tcp_rtx_queue树中,tcp是可靠的体现在这,只有收到ack确认以后,才会将buff从sk->tcp_rtx_queue中删除,否则会重传。
15. tcp_transmit_skb真正的数据发送函数,它首先记录当前时间到tp->tcp_wstamp_ns与skb->skb_mstamp_ns,生成一份skb的拷贝(注:它与原来的skb共享数据部分),接着就是填充tcp头,最后调用icsk->icsk_af_ops->queue_xmit(即:ipv4_specific:ip_queue_xmit)将skb发送出去,并将原skb通过skb->tcp_tsorted_anchor加入到tp->tsorted_sent_queue链表中,且会调用tcp_rate_skb_sent记录此时此tcp socket已经收到确认的包数量与时间(主要是:tp->delivered,tp->delivered_mstamp,tp->first_tx_mstamp变量)。
16. 设置超时定时器。
参考
linux内核版本:5.7.8