青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統(tǒng)計

最新評論

tcp connection setup的實現(xiàn)(二) --- 轉

首先來看下內核如何處理3次握手的半連接隊列和accept隊列(其實也就是server端的三次握手的狀態(tài)變換).而半連接隊列和accept隊列在內核如何表示,我們上次已經(jīng)介紹過了,這里就不介紹了.


首先我們知道當3層的數(shù)據(jù)包到達之后會調用4層的協(xié)議handle,tcp的話就是tcp_v4_rcv.如何調用可以看我前面的blog:

而在tcp_v4_rcv中,則最終會調用tcp_v4_do_rcv來處理輸入數(shù)據(jù)包.在看tcp_v4_do_rcv之前,我們先來看在tcp_v4_rcv中,內核如何通過4元組(目的,源端口和地址)來查找對應得sock對象.

在分析之前,我們要知道,當一對tcp連接3次握手完畢后,內核將會重新new一個socket,這個socket中的大部分域都是與主socket相同的.而把這個新的socket的狀態(tài)設置為established,而主socket的狀態(tài)依舊為listen狀態(tài).

而通過前面的blog分析,我們也知道在inet_hashinfo中將處于listening狀態(tài)的socket和處于 TCP_ESTABLISHED與TCP_CLOSE之間的狀態(tài)的socket是分開的,一個是ehash,一個是listening_hash.因此通 過對應的4元組查找socket也是分開在這兩個hash鏈表中操作的.

內核是通過調用__inet_lookup來查找socket的:

Java代碼  收藏代碼
  1. ///在tcp_v4_rcv中的代碼片段.  
  2. sk = __inet_lookup(net, &tcp_hashinfo, iph->saddr,  
  3.             th->source, iph->daddr, th->dest, inet_iif(skb));  
  4.   
  5. static inline struct sock *__inet_lookup(struct net *net,  
  6.                      struct inet_hashinfo *hashinfo,  
  7.                      const __be32 saddr, const __be16 sport,  
  8.                      const __be32 daddr, const __be16 dport,  
  9.                      const int dif)  
  10. {  
  11.     u16 hnum = ntohs(dport);  
  12.     struct sock *sk = __inet_lookup_established(net, hashinfo,  
  13.                 saddr, sport, daddr, hnum, dif);  
  14.   
  15.     return sk ? : __inet_lookup_listener(net, hashinfo, daddr, hnum, dif);  
  16. }  


tcp_hashinfo我們前面也已經(jīng)分析過了,包含了所有tcp所用到的hash信息,比如socket,port等等.這里的查找其實就是在tcp_hashinfo中(其實是它的域ehash或者listening_hash)查找相應的socket.

我們可以看到內核在這里進行了兩次查找,首先是在established狀態(tài)的socket中查找,處于established狀態(tài),說明3次握手已經(jīng)完成,因此這個socket可以通過簡單的4元組hash在hashinfo的ehash中查找.

而當在__inet_lookup_established中沒有找到時,則將會__inet_lookup_listener中查找.也就是在處于listening狀態(tài)的socket中查找(這里主要是通過daddr也就是目的地址來進行匹配).


當找到對應的socket以后就會進入數(shù)據(jù)包的處理,也就是進入tcp_v4_do_rcv函數(shù).


Java代碼  收藏代碼
  1. int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     struct sock *rsk;  
  4. ..................................................  
  5.   
  6. ///如果為TCP_ESTABLISHED狀態(tài),則進入相關處理  
  7.     if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */  
  8.         TCP_CHECK_TIMER(sk);  
  9.         if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {  
  10.             rsk = sk;  
  11.             goto reset;  
  12.         }  
  13.         TCP_CHECK_TIMER(sk);  
  14.         return 0;  
  15.     }  
  16.   
  17. ///進行包頭的合法性校驗.  
  18.     if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))  
  19.         goto csum_err;  
  20. ///進入TCP_LISTEN狀態(tài).  
  21.     if (sk->sk_state == TCP_LISTEN) {  
  22.         struct sock *nsk = tcp_v4_hnd_req(sk, skb);  
  23.         if (!nsk)  
  24.             goto discard;  
  25.   
  26.         if (nsk != sk) {  
  27.             if (tcp_child_process(sk, nsk, skb)) {  
  28.                 rsk = nsk;  
  29.                 goto reset;  
  30.             }  
  31.             return 0;  
  32.         }  
  33.     }  
  34.   
  35.     TCP_CHECK_TIMER(sk);  
  36. ///進入其他狀態(tài)的處理.除了ESTABLISHED和TIME_WAIT狀態(tài).  
  37.     if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {  
  38.         rsk = sk;  
  39.         goto reset;  
  40.     }  
  41.     TCP_CHECK_TIMER(sk);  
  42.     return 0;  
  43. ......................................................................  
  44. }  


可以看到當進來之后,會通過判斷socket的不同狀態(tài)來進入不同的處理.這里其實就分了3種狀態(tài),TCP_ESTABLISHED,TCP_LISTEN和剩余的的狀態(tài).

我們這里先不分析TCP_ESTABLISHED.

我們先來看當?shù)谝粋€syn分解到達后,內核會做怎么樣處理.首先它會進入tcp_v4_hnd_req函數(shù),這個函數(shù)我們后面會處理,這里只需要 知道當為第一個syn分節(jié)時,它會返回當前socket.因此此時nsk == sk,所以我們進入tcp_rcv_state_process函數(shù),這個函數(shù)處理除了ESTABLISHED和TIME_WAIT狀態(tài)之外的所有狀態(tài).

我們這里只看他的listen狀態(tài)處理,后面的話也是遇到一個狀態(tài),我們看一個狀態(tài)的處理:


Java代碼  收藏代碼
  1. int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,  
  2.               struct tcphdr *th, unsigned len)  
  3. {  
  4.     struct tcp_sock *tp = tcp_sk(sk);  
  5. ///取得對應的inet_connection_sock .  
  6.     struct inet_connection_sock *icsk = inet_csk(sk);  
  7.     int queued = 0;  
  8.     tp->rx_opt.saw_tstamp = 0;  
  9.   
  10.     switch (sk->sk_state) {  
  11.     case TCP_LISTEN:  
  12. ///當為ack分節(jié),則返回1,而對應內核會發(fā)送一個rst給對端.  
  13.         if (th->ack)  
  14.             return 1;  
  15. ///如果是rst,則忽略這個分組.  
  16.         if (th->rst)  
  17.             goto discard;  
  18. ///是syn分組,因此調用對應的虛函數(shù)conn_request,而這個函數(shù)在tcpv4中被初始化為tcp_v4_conn_request.  
  19.         if (th->syn) {  
  20.             if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)  
  21.                 return 1;  
  22.             kfree_skb(skb);  
  23.             return 0;  
  24.         }  
  25.         goto discard;  
  26. ............................................................  
  27. }  


可以看到最終會調用tcp_v4_conn_request來處理syn分組,我們接下來就來看這個函數(shù)的實現(xiàn).

先來看幾個相關的函數(shù),第一個是reqsk_queue_is_full,他來判斷半連接隊列是否已滿.其實實現(xiàn)很簡單,就是判斷qlen和max_qlen_log的大小:

Java代碼  收藏代碼
  1. static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)  
  2. {  
  3.     return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;  
  4. }  


第二個是sk_acceptq_is_full,它用來判斷accept隊列是否已滿.這個也是很簡單,比較當前的隊列大小sk_ack_backlog與最大的隊列大小sk_max_ack_backlog.

Java代碼  收藏代碼
  1. static inline int sk_acceptq_is_full(struct sock *sk)  
  2. {  
  3.     return sk->sk_ack_backlog > sk->sk_max_ack_backlog;  
  4. }  


最后一個是tcp_openreq_init,它用來新建一個inet_request_sock,我們知道每次一個syn到達后,我們都會新建一個inet_request_sock,并加入到半連接隊列.

Java代碼  收藏代碼
  1. static inline void tcp_openreq_init(struct request_sock *req,  
  2.                     struct tcp_options_received *rx_opt,  
  3.                     struct sk_buff *skb)  
  4. {  
  5.     struct inet_request_sock *ireq = inet_rsk(req);  
  6.   
  7.     req->rcv_wnd = 0;        /* So that tcp_send_synack() knows! */  
  8.     req->cookie_ts = 0;  
  9.     tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;  
  10.     req->mss = rx_opt->mss_clamp;  
  11.     req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;  
  12.     ireq->tstamp_ok = rx_opt->tstamp_ok;  
  13.     ireq->sack_ok = rx_opt->sack_ok;  
  14.     ireq->snd_wscale = rx_opt->snd_wscale;  
  15.     ireq->wscale_ok = rx_opt->wscale_ok;  
  16.     ireq->acked = 0;  
  17.     ireq->ecn_ok = 0;  
  18.     ireq->rmt_port = tcp_hdr(skb)->source;  
  19. }  



接下來來看tcp_v4_conn_request的實現(xiàn),
Java代碼  收藏代碼
  1. int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     struct inet_request_sock *ireq;  
  4.     struct tcp_options_received tmp_opt;  
  5.     struct request_sock *req;  
  6.     __be32 saddr = ip_hdr(skb)->saddr;  
  7.     __be32 daddr = ip_hdr(skb)->daddr;  
  8. ///這個名字實在是無語,when具體表示什么不太理解,只是知道它是用來計算rtt的.  
  9.     __u32 isn = TCP_SKB_CB(skb)->when;  
  10.     struct dst_entry *dst = NULL;  
  11. #ifdef CONFIG_SYN_COOKIES  
  12.     int want_cookie = 0;  
  13. #else  
  14. #define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */  
  15. #endif  
  16.   
  17. ///如果是廣播或者多播,則丟掉這個包.  
  18.     if (skb->rtable->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))  
  19.         goto drop;  
  20.   
  21. ///判斷半連接隊列是否已經(jīng)滿掉.如果滿掉并且處于非timewait狀態(tài),則丟掉這個包(如果設置了SYN Cookie則會繼續(xù)進行,因為SYN Cookie不需要新分配半連接隊列,詳細的SYN Cookie請google)  
  22.     if (inet_csk_reqsk_queue_is_full(sk) && !isn) {  
  23. #ifdef CONFIG_SYN_COOKIES  
  24.         if (sysctl_tcp_syncookies) {  
  25.             want_cookie = 1;  
  26.         } else  
  27. #endif  
  28.         goto drop;  
  29.     }  
  30. ///如果accept隊列已滿,并且qlen_young大于一就丟掉這個包,這里qlen_young大于一表示在syn隊列中已經(jīng)有足夠多的(這里不包括重傳的syn)請求了.  
  31.     if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)  
  32.         goto drop;  
  33.     req = inet_reqsk_alloc(&tcp_request_sock_ops);  
  34.     if (!req)  
  35.         goto drop;  
  36. ...................................................  
  37.   
  38. ///對tmp_opt進行初始化,而tcp_options_received中包含了tcp的一些選項信息(比如mss,窗口擴大因子等等)  
  39.     tcp_clear_options(&tmp_opt);  
  40.     tmp_opt.mss_clamp = 536;  
  41.     tmp_opt.user_mss  = tcp_sk(sk)->rx_opt.user_mss;  
  42.   
  43. ///對對端的tcp_options_received進行解析,并對本端得tcp_options_received進行初始化.  
  44.     tcp_parse_options(skb, &tmp_opt, 0);  
  45.   
  46. .......................................................  
  47. ///這里對新的req進行初始化.  
  48.   
  49.     tcp_openreq_init(req, &tmp_opt, skb);  
  50. ...............................................  
  51.   
  52. ///這里將tcp_options_received保存到req中.  
  53.     ireq->opt = tcp_v4_save_options(sk, skb);  
  54.     if (!want_cookie)  
  55.         TCP_ECN_create_request(req, tcp_hdr(skb));  
  56.   
  57.     if (want_cookie) {  
  58. #ifdef CONFIG_SYN_COOKIES  
  59.         syn_flood_warning(skb);  
  60.         req->cookie_ts = tmp_opt.tstamp_ok;  
  61. #endif  
  62.         isn = cookie_v4_init_sequence(sk, skb, &req->mss);  
  63.     }else if (!isn) {  
  64. .............................................  
  65. ///計算當前一個合適的isn,并返回.  
  66.         isn = tcp_v4_init_sequence(skb);  
  67.     }  
  68.   
  69. ///賦值發(fā)送給對端的isn  
  70.     tcp_rsk(req)->snt_isn = isn;  
  71.   
  72. ///發(fā)送syn和ack(如果設置了want_cookie則不會將這個req鏈接到半連接隊列中.  
  73.     if (__tcp_v4_send_synack(sk, req, dst) || want_cookie)  
  74.         goto drop_and_free;  
  75.   
  76. ///將這個req鏈接到半連接隊列中.  
  77.     inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);  
  78.     return 0;  
  79.   
  80. drop_and_release:  
  81.     dst_release(dst);  
  82. drop_and_free:  
  83.     reqsk_free(req);  
  84. drop:  
  85.     return 0;  
  86. }  



而tcp_v4_hnd_req的主要工作是在半連接隊列中看是否存在當前的socket,如果存在則說明這個有可能是最終的ack包,因此將會 做一系列的合法性校驗(比如重傳,rst,syn等等),最終確定這個是ack后會調用對應的新建socket的虛函數(shù)syn_recv_sock.

Java代碼  收藏代碼
  1. static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     struct tcphdr *th = tcp_hdr(skb);  
  4.     const struct iphdr *iph = ip_hdr(skb);  
  5.     struct sock *nsk;  
  6.     struct request_sock **prev;  
  7. ///通過socket,查找對應request_sock  
  8.     struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,  
  9.                                iph->saddr, iph->daddr);  
  10.     if (req)  
  11. ///如果存在則進入req的相關處理.  
  12.         return tcp_check_req(sk, skb, req, prev);  
  13.   
  14. ///不存在,則通過inet_lookup_established查找.這是因為有可能當我們進入這個函數(shù)之前,socket的狀態(tài)被改變了,也就是這個socket的狀態(tài)已經(jīng)不是listen了.  
  15.   
  16.     nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr,  
  17.             th->source, iph->daddr, th->dest, inet_iif(skb));  
  18.   
  19.     if (nsk) {  
  20.         if (nsk->sk_state != TCP_TIME_WAIT) {  
  21. ///非tw狀態(tài)返回新的socket.  
  22.             bh_lock_sock(nsk);  
  23.             return nsk;  
  24.         }  
  25. ///如果是timewait狀態(tài)則返回空.  
  26.         inet_twsk_put(inet_twsk(nsk));  
  27.         return NULL;  
  28.     }  
  29.   
  30. #ifdef CONFIG_SYN_COOKIES  
  31.     if (!th->rst && !th->syn && th->ack)  
  32.         sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));  
  33. #endif  
  34.     return sk;  
  35. }  



tcp_check_req最主要工作就是調用虛函數(shù),新建一個socket,并返回.

先來看幾個相關的函數(shù),第一個是inet_csk_reqsk_queue_unlink,它主要用來從半連接隊列unlink掉一個元素.:

Java代碼  收藏代碼
  1. static inline void inet_csk_reqsk_queue_unlink(struct sock *sk,  
  2.                            struct request_sock *req,  
  3.                            struct request_sock **prev)  
  4. {  
  5.     reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req, prev);  
  6. }  
  7.   
  8. static inline void reqsk_queue_unlink(struct request_sock_queue *queue,  
  9.                       struct request_sock *req,  
  10.                       struct request_sock **prev_req)  
  11. {  
  12.     write_lock(&queue->syn_wait_lock);  
  13. ///處理鏈表.  
  14.     *prev_req = req->dl_next;  
  15.     write_unlock(&queue->syn_wait_lock);  
  16. }  


第二個是inet_csk_reqsk_queue_removed,它主要用來修改對應的qlen和qlen_young的值.


Java代碼  收藏代碼
  1. static inline void inet_csk_reqsk_queue_removed(struct sock *sk,  
  2.                         struct request_sock *req)  
  3. {  
  4.     if (reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req) == 0)  
  5.         inet_csk_delete_keepalive_timer(sk);  
  6. }  
  7.   
  8. static inline int reqsk_queue_removed(struct request_sock_queue *queue,  
  9.                       struct request_sock *req)  
  10. {  
  11.     struct listen_sock *lopt = queue->listen_opt;  
  12. ///如果重傳數(shù)為0則說明沒有重傳過,因此qlen_young跟著也減一.  
  13.     if (req->retrans == 0)  
  14.         --lopt->qlen_young;  
  15.   
  16.     return --lopt->qlen;  
  17. }  


最后是inet_csk_reqsk_queue_add,它用來把新的req加入到accept隊列中.


Java代碼  收藏代碼
  1. static inline void inet_csk_reqsk_queue_add(struct sock *sk,  
  2.                         struct request_sock *req,  
  3.                         struct sock *child)  
  4. {  
  5.     reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);  
  6. }  
  7.   
  8.   
  9. static inline void reqsk_queue_add(struct request_sock_queue *queue,  
  10.                    struct request_sock *req,  
  11.                    struct sock *parent,  
  12.                    struct sock *child)  
  13. {  
  14.     req->sk = child;  
  15.     sk_acceptq_added(parent);  
  16. ///可以看到剛好就是request_sock_queue的rskq_accept_head與rskq_accept_tail保存accept隊列.  
  17.     if (queue->rskq_accept_head == NULL)  
  18.         queue->rskq_accept_head = req;  
  19.     else  
  20.         queue->rskq_accept_tail->dl_next = req;  
  21.   
  22.     queue->rskq_accept_tail = req;  
  23.     req->dl_next = NULL;  
  24. }  


然后再來看tcp_check_req的實現(xiàn).
Java代碼  收藏代碼
  1. struct sock *tcp_check_req(struct sock *sk,struct sk_buff *skb,  
  2.                struct request_sock *req,  
  3.                struct request_sock **prev)  
  4. {  
  5.     const struct tcphdr *th = tcp_hdr(skb);  
  6.     __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);  
  7.     int paws_reject = 0;  
  8.     struct tcp_options_received tmp_opt;  
  9.     struct sock *child;  
  10.   
  11.     tmp_opt.saw_tstamp = 0;  
  12. ......................................  
  13. ///如果只有rst和syn域則發(fā)送一個rst給對端.  
  14. if (flg & (TCP_FLAG_RST|TCP_FLAG_SYN)) {  
  15.         TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);  
  16.         goto embryonic_reset;  
  17.     }  
  18.   
  19. ///如果是重傳的syn,則重新發(fā)送syn和ack分組.  
  20.     if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn &&  
  21.         flg == TCP_FLAG_SYN &&  
  22.         !paws_reject) {  
  23.         req->rsk_ops->rtx_syn_ack(sk, req);  
  24.         return NULL;  
  25.     }  
  26.   
  27.     ..........................................  
  28.   
  29. ///確定有設置ack分節(jié).  
  30.     if (!(flg & TCP_FLAG_ACK))  
  31.         return NULL;  
  32.   
  33. ///這里主要處理TCP_DEFER_ACCEPT被設置的情況,如果它被設置,則丟掉這個包.(這是因為TCP_DEFER_ACCEPT會等待數(shù)據(jù)真正發(fā)過來才處理的,而不是最后一個ack發(fā)過來就處理)  
  34.     if (inet_csk(sk)->icsk_accept_queue.rskq_defer_accept &&  
  35.         TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) {  
  36.         inet_rsk(req)->acked = 1;  
  37.         return NULL;  
  38.     }  
  39.   
  40. ///可以創(chuàng)建一個新的socket了.返回一個包含新創(chuàng)建的socket的request結構.  
  41.     child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);  
  42.     if (child == NULL)  
  43.         goto listen_overflow;  
  44. ..................................  
  45. #endif  
  46. ///創(chuàng)建成功,則在request_sock_queue的listen_opt中unlink掉這個req.也就是從半連接隊列中刪除這個req.  
  47.     inet_csk_reqsk_queue_unlink(sk, req, prev);  
  48. ///修改對應的 qlen和qlen_young的值.  
  49.     inet_csk_reqsk_queue_removed(sk, req);  
  50. ///最后加入到accept隊列中.這里注意最終是將新的socket賦值給對應的req.  
  51.     inet_csk_reqsk_queue_add(sk, req, child);  
  52.     return child;  
  53.   
  54. listen_overflow:  
  55.     if (!sysctl_tcp_abort_on_overflow) {  
  56.         inet_rsk(req)->acked = 1;  
  57.         return NULL;  
  58.     }  
  59.   
  60. embryonic_reset:  
  61.     NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);  
  62.     if (!(flg & TCP_FLAG_RST))  
  63.         req->rsk_ops->send_reset(sk, skb);  
  64.   
  65.     inet_csk_reqsk_queue_drop(sk, req, prev);  
  66.     return NULL;  
  67. }  



最后我們來看內核如何創(chuàng)建一個新的socket,tcp 協(xié)議使用tcp_v4_syn_recv_sock來實現(xiàn),它做的其實很簡單就是新建一個socket,并且設置狀態(tài)為TCP_SYN_RECV(在 inet_csk_clone中),父socket繼續(xù)處于listen狀態(tài),然后對新的socket進行一些賦值,然后對一些定時器進行初始化.這里定 時器我們全部都略過了,以后會專門來分析tcp中的定時器.


最后從tcp_v4_hnd_req中返回,判斷是否與父socket相等,然后調用tcp_child_process函數(shù):

這個函數(shù)主要是完成最終的三次握手,將子socket設置為TCP_ESTABLISHED然后根據(jù)條件喚醒被accept阻塞的主socket:

Java代碼  收藏代碼
  1. int tcp_child_process(struct sock *parent, struct sock *child,  
  2.               struct sk_buff *skb)  
  3. {  
  4.     int ret = 0;  
  5.     int state = child->sk_state;  
  6.   
  7.     if (!sock_owned_by_user(child)) {  
  8. ///完成最終的三次握手.  
  9.         ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb),  
  10.                         skb->len);  
  11.         /* Wakeup parent, send SIGIO */  
  12.         if (state == TCP_SYN_RECV && child->sk_state != state)  
  13. ///喚醒阻塞的主socket.  
  14.             parent->sk_data_ready(parent, 0);  
  15.     } else {  
  16.         /* Alas, it is possible again, because we do lookup 
  17.          * in main socket hash table and lock on listening 
  18.          * socket does not protect us more. 
  19.          */  
  20.         sk_add_backlog(child, skb);  
  21.     }  
  22.   
  23.     bh_unlock_sock(child);  
  24.     sock_put(child);  
  25.     return ret;  
  26. }  


最后來分析下在tcp_rcv_state_process中的處理當前的TCP_SYN_RECV狀態(tài),它主要是為將要到來的數(shù)據(jù)傳輸做一些準備,設置一些相關域.:


Java代碼  收藏代碼
  1. case TCP_SYN_RECV:  
  2.             if (acceptable) {  
  3.                 tp->copied_seq = tp->rcv_nxt;  
  4.                 smp_mb();  
  5. ///設置狀態(tài)為TCP_ESTABLISHED.  
  6.                 tcp_set_state(sk, TCP_ESTABLISHED);  
  7.                 sk->sk_state_change(sk);  
  8.   
  9. ///這里的wake應該是針對epoll這類的  
  10.                 if (sk->sk_socket)  
  11.                     sk_wake_async(sk,  
  12.                               SOCK_WAKE_IO, POLL_OUT);  
  13.   
  14. ///設置期望接收的isn號,也就是第一個字節(jié)的序列和窗口大小.  
  15.                 tp->snd_una = TCP_SKB_CB(skb)->ack_seq;  
  16.                 tp->snd_wnd = ntohs(th->window) <<  
  17.                           tp->rx_opt.snd_wscale;  
  18.                 tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq,  
  19.                         TCP_SKB_CB(skb)->seq);  
  20.   
  21. .........................................................................  
  22.             break;  

posted on 2013-02-15 23:12 大龍 閱讀(631) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区黄色| 免费一级欧美片在线观看| 欧美中文字幕在线视频| 国产精品毛片| 亚洲欧美精品在线观看| 欧美在线播放| 精品av久久久久电影| 欧美aⅴ99久久黑人专区| 欧美激情一区| 亚洲综合精品| 欧美18av| 欧美日产国产成人免费图片| 亚洲最新在线视频| 性8sex亚洲区入口| 在线看片一区| 午夜欧美精品久久久久久久| 在线视频观看日韩| 亚洲精品视频免费观看| 国产亚洲欧美一区二区三区| 欧美成人一区二区三区在线观看| 国产精品久久久爽爽爽麻豆色哟哟| 久久精品欧美| 免费一级欧美片在线播放| 欧美亚洲视频一区二区| 噜噜噜噜噜久久久久久91| 亚洲最新视频在线| 蜜桃av一区二区| 亚洲在线观看视频网站| 欧美阿v一级看视频| 欧美亚洲午夜视频在线观看| 亚洲欧美日韩久久精品| 99热精品在线观看| 欧美亚洲一区二区在线| 亚洲欧美日韩精品综合在线观看| 久久久一区二区| 亚洲欧美视频在线观看| 欧美日韩国产美| 母乳一区在线观看| 欧美黑人多人双交| 老巨人导航500精品| 国产精品播放| 一区二区三区欧美亚洲| 亚洲黄网站在线观看| 小黄鸭精品密入口导航| 亚洲天堂av高清| 中日韩美女免费视频网址在线观看 | 亚洲精品免费网站| 亚洲一区二区网站| 亚洲一区免费看| 欧美成人蜜桃| 亚洲国产专区校园欧美| 国产一区亚洲| 亚洲国产精品久久人人爱蜜臀| 在线观看一区| 久久久久在线观看| 国产精品免费电影| 亚洲全部视频| 亚洲高清在线视频| 久久综合给合| 美女视频网站黄色亚洲| 欧美日韩国产麻豆| 一区二区三区精品视频| 亚洲美女在线看| 久久综合影视| 欧美成人一区二区三区在线观看 | 亚洲国产一区二区三区高清| 欧美一级电影久久| 久久黄色网页| 国产亚洲福利社区一区| 亚洲欧美日本国产专区一区| 久久国产精品99精品国产| 国产精品二区三区四区| 亚洲免费高清| 性刺激综合网| 国产一区二区三区精品久久久| 久久riav二区三区| 久久一区二区三区四区五区| 国内精品视频666| 能在线观看的日韩av| 亚洲第一天堂无码专区| 这里只有精品视频| 国产精品swag| 久久国产欧美| 欧美二区视频| 亚洲影视综合| 国产一区自拍视频| 欧美不卡福利| 宅男噜噜噜66一区二区| 欧美一区二区三区在| 亚洲福利在线看| 欧美人在线视频| 西瓜成人精品人成网站| 蜜桃av噜噜一区二区三区| 一本一本大道香蕉久在线精品| 欧美视频成人| 亚洲一区精彩视频| 免费欧美在线视频| 日韩视频―中文字幕| 国产日韩欧美高清免费| 久久亚洲精品欧美| 亚洲视频精品在线| 久久女同精品一区二区| 国产午夜久久| 欧美激情一二三区| 亚洲欧美日韩国产成人| 亚洲片区在线| 久久久久久久久岛国免费| 一区二区三区黄色| 国产主播一区二区三区四区| 久久久精品国产免大香伊| 一区二区三区回区在观看免费视频| 久久久国产午夜精品| 日韩一级片网址| 国产一区二区三区黄视频| 欧美成黄导航| 亚洲在线播放| 亚洲人午夜精品| 欧美激情自拍| 久久综合九九| 亚洲一区二区三区视频播放| 亚洲激情电影在线| 久久成人av少妇免费| 一区二区三区四区五区精品视频| 夜夜嗨网站十八久久| 午夜在线一区二区| 一区二区三区久久网| 欧美v日韩v国产v| 久久久久成人精品免费播放动漫| 亚洲乱码精品一二三四区日韩在线 | 久久综合给合久久狠狠色| 亚洲美女中文字幕| 国产一区白浆| 国产欧美日韩视频在线观看| 欧美精选一区| 西西裸体人体做爰大胆久久久| 亚洲一级黄色| 欧美国产视频一区二区| 男男成人高潮片免费网站| 欧美一区二区三区免费观看视频| 亚洲一区视频在线观看视频| 日韩一区二区精品在线观看| 亚洲国产mv| 亚洲国产一区二区三区a毛片 | 欧美激情一区二区在线| 蜜桃av噜噜一区| 久久久久国内| 久久久一区二区三区| 午夜精品视频在线观看| 亚洲精品国产精品国自产观看浪潮 | 亚洲欧美日韩在线综合| 裸体一区二区| 欧美在线免费| 午夜宅男欧美| 欧美亚洲综合在线| 性欧美18~19sex高清播放| 亚洲欧美国产精品专区久久| 先锋资源久久| 久久噜噜亚洲综合| 欧美黄色一区| 欧美三级精品| 国产精品一区二区欧美| 国产一区二区三区视频在线观看| 国产亚洲欧洲997久久综合| 精品白丝av| 亚洲精品欧洲| 午夜视频在线观看一区| 久久久不卡网国产精品一区| 免费在线观看成人av| 亚洲第一免费播放区| 亚洲精品久久| 亚洲欧美日韩在线综合| 久久久久久久久蜜桃| 欧美日本在线观看| 欧美三区在线视频| 国产一区二区三区在线播放免费观看| 激情综合网激情| 国产一区在线看| 午夜影院日韩| 欧美mv日韩mv国产网站| 欧美午夜精品久久久久免费视| 国产欧美一区二区三区在线老狼| 韩国精品一区二区三区| 一本色道久久综合亚洲二区三区| 性视频1819p久久| 亚洲成色777777女色窝| 中文在线一区| 久久人人97超碰人人澡爱香蕉| 国产精品vvv| 一本久道久久综合婷婷鲸鱼| 亚洲第一偷拍| 亚洲一区二区在| 榴莲视频成人在线观看| 亚洲美女一区| 久久精品动漫| 欧美三级电影大全| 亚洲国产毛片完整版| 欧美淫片网站| 亚洲人成网站影音先锋播放| 久久久国产精品一区| 欧美色偷偷大香|