Linux内核分析 - 网络[十六]:TCP三次握手
状态TCP_SYN_RECV的设置可能比较奇怪,按照TCP 的状态转移图,在服务端收到SYN报文后变迁为TCP_SYN_RECV,但看到在实现中收到ACK后才有了状态TCP_SYN_RECV,并且马上会 变为TCP_ESTABLISHED,所以这个状态变得无足轻重。这样做的原因是listen和accept返回的socket是不同的,而只有真正连接 建立时才会创建这个新的socket,在收到SYN报文时新的socket还没有建立,就无从谈状态变迁了。这里同样是一个平衡的存在 ,你也可以在收到SYN时创建一个新的socket,代价就是无用的socket大大增加了。 child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); if (child == NULL) goto listen_overflow; inet_csk_reqsk_queue_unlink(sk, req, prev); inet_csk_reqsk_queue_removed(sk, req); inet_csk_reqsk_queue_add(sk, req, child); tcp_child_process() 如果此时sock: child被用户进程锁住了,那么就先添加到backlog中__sk_add_backlog(),待解锁时再处理backlog上的sock;如果此时没有被 锁住,则先调用tcp_rcv_state_process()进行处理,处理完后,如果child状态到达TCP_ESTABLISHED,则表明其已就绪,调用 sk_data_ready()唤醒等待在isck_accept_queue上的函数accept()。 if (! sock_owned_by_user(child)) { ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); if (state == TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready(parent, 0); } else { __sk_add_backlog(child, skb); } tcp_rcv_state_process()处理各个状态上socket的情况。下面是处于TCP_SYN_RECV的代码段,注意此时传入函数的 sk已经是新创建的sock了(在tcp_v4_hnd_req()中),并且状态是TCP_SYN_RECV,而不再是listen socket,在收到ACK后,sk状态 变迁为TCP_ESTABLISHED,而在tcp_v4_hnd_req()中也已将sk插入到了icsk_accept_queue上,此时它就已经完全就绪了,回到 tcp_child_process()便可执行sk_data_ready()。 case TCP_SYN_RECV: if (acceptable) { …… tcp_set_state(sk, TCP_ESTABLISHED); sk->sk_state_change(sk); …… tp->snd_una = TCP_SKB_CB(skb)->ack_seq; tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); …… } 最后总结三次握手的过程 (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |