http://blog.csdn.net/zhangskd/article/details/17923917
分类: Linux TCP/IP Linux Kernel 2014-01-07 09:46 2311人阅读 评论(2) 收藏 举报

目录(?)[+]

本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径。

内核版本:3.6

Author:zhangskd @ csdn blog

函数路径

以下是第三次握手时,服务端接收到ACK后的处理路径。

接收入口

1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。

2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。

3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。

  1. int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
  2. {
  3. struct sock *rsk;
  4. #ifdef CONFIG_TCP_MD5SIG
  5. /* We really want to reject the packet as early as possible if :
  6. * We're expecting an MD5'd packet and this is no MD5 tcp option.
  7. * There is an MD5 option and we're not expecting one.
  8. */
  9. if (tcp_v4_inbound_md5_hash(sk, skb))
  10. goto discard;
  11. #endif
  12. /* 当状态为ESTABLISHED时,用tcp_rcv_established()接收处理 */
  13. if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
  14. struct dst_entry *dst = sk->sk_rx_dst;
  15. sock_rps_save_rxhash(sk, skb);
  16. if (dst) {
  17. if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, 0) == NULL) {
  18. dst_release(dst);
  19. sk->sk_rx_dst = NULL;
  20. }
  21. }
  22. /* 连接已建立时的处理路径 */
  23. if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
  24. rsk = sk;
  25. goto reset;
  26. }
  27. return 0;
  28. }
  29. /* 检查报文长度、报文校验和 */
  30. if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
  31. goto csum_err;
  32. /* 如果这个sock处于监听状态,被动打开时的处理,包括收到SYN或ACK */
  33. if (sk->sk_state == TCP_LISTEN) {
  34. /* 返回值:
  35. * NULL,错误
  36. * nsk == sk,接收到SYN
  37. * nsk != sk,接收到ACK
  38. */
  39. struct sock *nsk = tcp_v4_hnd_req(sk, skb); /* 接收ACK的处理 */
  40. if (! nsk)
  41. goto discard;
  42. if (nsk != sk) { /* 接收到ACK时 */
  43. sock_rps_save_rxhash(nsk, skb);
  44. if (tcp_child_process(sk, nsk, skb)) { /* 处理新的sock */
  45. rsk = nsk;
  46. goto reset;
  47. }
  48. return 0;
  49. }
  50. } else
  51. sock_rps_save_rx(sk, skb);
  52. /* 处理除了ESTABLISHED和TIME_WAIT之外的所有状态 */
  53. if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
  54. rsk = sk;
  55. goto reset;
  56. }
  57. return 0;
  58. reset:
  59. tcp_v4_send_reset(rsk, skb); /* 发送RST包 */
  60. discard:
  61. kfree_skb(skb);
  62. return 0;
  63. csum_err:
  64. TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
  65. goto discard;
  66. }

收到SYN段后,服务器端会分配一个连接请求块,并初始化这个连接请求块。

构造和发送SYNACK段。

然后把这个连接请求块链入半连接队列中,启动超时定时器。

之后如果再收到ACK,就能完成三次握手了。

  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. /* 在半连接队列中查找是否已有符合的连接请求块,如果有,则说明这是三次握手的最后一个ACK。*/
  8. struct request_sock *req = inet_csk_search_req(sk, &prev, th->source, iph->saddr, iph->daddr);
  9. if (req)
  10. return tcp_check_req(sk, skb, req, prev); /* 服务器端处理三次握手的最后一个ACK */
  11. /* 如果在半连接队列中没找到,则在ESTABLISHED状态的哈希表中查找。*/
  12. nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source,
  13. iph->daddr, th->dest, inet_iif(skb));
  14. if (nsk) { /* 如果在ehash表中找到对应的sock,且不处于TIME_WAIT状态 */
  15. if (nsk->sk_state != TCP_TIME_WAIT) {
  16. bh_lock_sock(nsk);
  17. return nsk;
  18. }
  19. inet_twsk_put(inet_twsk(nsk)); /* 释放tw结构体 */
  20. return NULL;
  21. }
  22. #ifdef CONFIG_SYN_COOKIES
  23. /* 如果使用SYN Cookie,则检查cookie是否合法,合法则直接完成三次握手 */
  24. if (! th->syn)
  25. sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
  26. #endif
  27. return sk;
  28. }

在表示半连接队列的哈希表中,寻找符合条件的连接请求块。

  1. struct request_sock *inet_csk_search_req(const struct sock *sk, struct request_sock ***prevp,
  2. const __be16 rport, const __be32 raddr, const __be32 laddr)
  3. {
  4. const struct inet_connection_sock *icsk = inet_csk(sk);
  5. struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; /* 半连接队列 */
  6. struct request_sock *req, **prev;
  7. /* 通过哈希值,找到哈希桶,然后遍历哈希桶寻找符合条件的连接请求块 */
  8. for(prev = &lopt->syn_table[inet_synq_hash(raddr, rport, lopt->hash_rnd, lopt->nr_table_entries)];
  9. (req = *prev) != NULL; prev = &req->dl_next) {
  10. const struct inet_request_sock *ireq = inet_rsk(req);
  11. if (ireq->rmt_port == rport && ireq->rmt_addr == raddr && ireq->loc_addr = laddr
  12. && AF_INET_FAMILY(req->rsk_ops->family)) {
  13. WARN_ON(req->sk); /* 连接尚未建立,sk应该为NULL */
  14. *prevp = prev; /* 保存此req指针的指针 */
  15. break;
  16. }
  17. }
  18. return req;
  19. }

第三次握手

inet_csk_search_req()在半连接队列中查找是否已有符合的连接请求块,如果有,则说明这可能是三次握手的最后一个ACK。

接着调用tcp_check_req()来进行验证,如果合法,则完成三次握手。

  1. /* Process an incoming packet for SYN_RECV sockets represented as a request_sock. */
  2. struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, struct request_sock *req,
  3. struct request_sock **prev)
  4. {
  5. struct tcp_options_received tmp_opt;
  6. const u8 *hash_location;
  7. struct sock *child;
  8. const struct tcphdr *th = tcp_hdr(skb);
  9. __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_ACK);
  10. bool paws_reject = false;
  11. tmp_opt.saw_tstamp = 0;
  12. /* 如果此ACK带有选项 */
  13. if (th->doff > (sizeof(struct tcphdr) >> 2)) {
  14. tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); /* 解析TCP选项,保存到实例中 */
  15. if (tmp_opt.saw_tstamp) {
  16. tmp_opt.ts_recent = req->ts_recent; /* 客户端发送SYN段的时间 */
  17. /* We do not store true stamp, but it is not required,
  18. * it can be estimated (approximately) from another data.
  19. */
  20. tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ) << req->retrans);
  21. paws_reject = tcp_paws_reject(&tmp_opt, th->rst); /* 检查客户端时间戳是否回绕 */
  22. }
  23. }
  24. /* Check for pure retransmitted SYN. 处理重传的SYN */
  25. if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn && flg == TCP_FLAG_SYN
  26. && ! paws_reject) {
  27. /* 重新发送SYNACK。
  28. * 实例为tcp_request_sock_ops,调用tcp_v4_rtx_synack()
  29. */
  30. req->rsk_ops->rtx_syn_ack(sk, req, NULL);
  31. return NULL;
  32. }
  33. /* 如果接收段包含ACK标志,但确认序号不对,则返回监听sock。
  34. * 然后在tcp_v4_do_rcv()中发送RST段。
  35. */
  36. if ((flg & TCP_FLAG_ACK) && (TCP_SKB_CB(skb)->ack_seq !=
  37. tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk))))
  38. return sk;
  39. /* 如果发生了回绕,或者接收序号不在接收窗口内 */
  40. if (paws_reject || ! tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
  41. tcp_rsk(req)->rcv_isn + 1, tcp_rsk(req)->rcv_isn + 1 + req->rcv_wnd)) {
  42. /* Out of window: send ACK and drop. */
  43. if (! (flg & TCP_FLAG_RST))
  44. /* 发送ACK段。
  45. * 实例为tcp_request_sock_ops,调用tcp_v4_reqsk_send_ack()
  46. */
  47. req->rsk_ops->send_ack(sk, skb, req);
  48. if (paws_reject)
  49. NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
  50. return NULL;
  51. }
  52. /* In sequence, PAWS is ok. */
  53. if (tmp_opt.saw_tstamp && ! after(TCP_SKB_CB(skb)->seq, tcp_rsk(req)->rcv_isn + 1))
  54. req->ts_recent = tmp_opt.rcv_tsval; /* 保存ACK段的时间戳 */
  55. if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn) {
  56. /* Truncate SYN, it is out of window starting at tcp_rsk(req)->rcv_isn + 1 */
  57. flg &= ~TCP_FLAG_SYN;
  58. }
  59. if (flg & (TCP_FLAG_RST | TCP_FLAG_SYN)) {
  60. TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
  61. goto embryonic_reset;
  62. }
  63. /* ACK sequence verified above, just make sure ACK is set.
  64. * If ACK not set, just silently drop the packet.
  65. */
  66. if (! (flg & TCP_FLAG_ACK))
  67. return NULL;
  68. /* 如果设置了TCP_DEFER_ACCEPT选项,则不接收纯ACK,等待有负荷的数据包到达后,
  69. * 再建立连接。直接丢弃纯ACK。
  70. */
  71. if (req->retrans < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept &&
  72. TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) {
  73. inet_rsk(req)->acked = 1;
  74. NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP);
  75. return NULL;
  76. }
  77. if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr)
  78. tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr;
  79. else if (req->retrans) /* don't take RTT sample if retrans && ~TS */
  80. tcp_rsk(req)->snt_synack = 0;
  81. /* OK, ACK is valid, create big socket and feed this segment to it.
  82. * This segment must move socket to established state. If it will be dropped
  83. * after socket is created, wait for troubles.
  84. */
  85. /*  三次握手完成以后,调用tcp_v4_syn_recv_sock()创建和初始化一个新的传输控制块 */
  86. child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
  87. if (child == NULL)
  88. goto listen_overflow;
  89. inet_csk_reqsk_queue_unlink(sk, req, prev); /* 把连接请求块从半连接队列中删除 */
  90. inet_csk_reqsk_queue_removed(sk, req); /* 更新半连接队列的长度,如果为0,则删除定时器 */
  91. /* 把完成三次握手的连接请求块,和新的sock关联起来,并把它移入全连接队列中 */
  92. inet_csk_reqsk_queue_add(sk, req, child);
  93. return child;
  94. listen_overflow:
  95. /* tcp_abort_on_overflow表示全连接队列满了,是给客户端发RST段,还是默默丢弃 */
  96. if (! sysctl_tcp_abort_on_overflow) {
  97. inet_rsk(req)->acked = 1;
  98. return NULL;
  99. }
  100. embryonic_reset:
  101. NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);
  102. if (! (flg & TCP_FLAG_RST))
  103. /* 实例为tcp_request_sock_ops,调用tcp_v4_send_reset()。*/
  104. req->rsk_ops->send_reset(sk, skb);
  105. /* 把连接请求块从半连接队列中删除,更新半连接队列 */
  106. inet_csk_reqsk_queue_drop(sk, req, prev);
  107. return NULL;
  108. }

是否发生了回绕。

  1. static inline bool tcp_paws_reject(const struct tcp_options_received *rx_opt, int rst)
  2. {
  3. if (tcp_paws_check(rx_opt, 0))
  4. return false;
  5. /* ACK段包含RST标志 */
  6. if (rst && get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_MSL)
  7. return false;
  8. return true;
  9. }

检查客户端的时间戳是否合法。

要求客户端发送SYN的时间戳 <= 客户端重传SYN的时间戳 、客户端发送ACK的时间戳。

  1. static inline bool tcp_paws_check(const struct tcp_options_received *rx_opt, int paws_win)
  2. {
  3. if ((s32) (rx_opt->ts_recent - rx_opt->rcv_tsval) <= paws_win)
  4. return true;
  5. /* 重传时间超过24天?*/
  6. if (unlikely(get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS))
  7. return true;
  8. /* Some OSes send SYN and SYNACK messages with tsval = 0 tsecr = 0,
  9. * then following tcp messages have valid values. Ignore 0 value, or else 'negative'
  10. * tsval might forbid us to accept their packets.
  11. */
  12. if (! rx_opt->ts_recent)
  13. return true;
  14. }

检查序号是否合法。

  1. /* @seq:接收段的序号。
  2. * @end_seq:接收段的结束序号。
  3. * @s_win:接收窗口的起始序号。
  4. * @e_win:接收窗口的结束序号。
  5. */
  6. static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win)
  7. {
  8. if (seq == s_win)
  9. return true;
  10. if (after(end_seq, s_win) && before(seq, e_win))
  11. return true;
  12. return seq == e_win && seq == end_seq;
  13. }

连接请求块操作

request_sock_ops为处理连接请求块的函数指针表,对于TCP,它的实例为tcp_request_sock_ops。

  1. struct request_sock_ops tcp_request_sock_ops __read_mostly = {
  2. .family = PF_INET,
  3. .obj_size = sizeof(struct tcp_request_sock),
  4. .rtx_syn_ack = tcp_v4_rtx_synack, /* 重传SYNACK段 */
  5. .send_ack = tcp_v4_reqsk_send_ack, /* 发送ACK段 */
  6. .destructor = tcp_v4_reqsk_destructor,
  7. .send_reset = tcp_v4_send_reset, /* 发送RST段 */
  8. .syn_ack_timeout = tcp_syn_ack_timeout, /* SYNACK段超时处理 */
  9. };

(1) 重传SYNACK段

  1. static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req, struct request_values *rvp)
  2. {
  3. TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
  4. return tcp_v4_send_synack(sk, NULL, req, rvp, 0, false);
  5. }

我们在上一篇中已分析过tcp_v4_send_synack(),它主要用于构造和发送SYNACK段。

(2) 发送ACK段 

在tcp_check_req()中,如果接收到的ACK段时间戳不合法、或者序号不在接收窗口内,且不含RST标志,

则需要给客户端发送一个ACK。

  1. static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req)
  2. {
  3. tcp_v4_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
  4. req->rcv_wnd, req->ts_recent, 0,
  5. tcp_md5_do_lookup(sk, (union tcp_md5_addr *) &ip_hdr(skb)->daddr, AF_INET),
  6. inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos);
  7. }
  1. static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, int oif,
  2. struct tcp_md5sig_key *key, int reply_flags, u8 tos)
  3. {
  4. const struct tcphdr *th = tcp_hdr(skb);
  5. struct {
  6. struct tcphdr th;
  7. __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
  8. #ifdef CONFIG_TCP_MD5SIG
  9. + (TCPOLEN_MD5SIG_ALIGNED >> 2)
  10. #endif
  11. ];
  12. } rep;
  13. struct ip_reply_arg arg;
  14. struct net *net = dev_net(skb_dst(skb)->dev);
  15. memset(&rep.th, 0, sizeof(struct tcphdr));
  16. memset(&arg, 0, sizeof(arg));
  17. arg.iov[0].iov_base = (unsigned char *) &rep;
  18. arg.iov[0].iov_len = sizeof(rep.th);
  19. if (ts) { /* 时间戳 */
  20. rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) |
  21. TCPOLEN_TIMESTAMP);
  22. rep.opt[1] = htonl(tcp_time_stamp);
  23. rep.opt[2] = htonl(ts);
  24. arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED;
  25. }
  26. /* Swap the send and the receive. */
  27. rep.th.dest = th->source;
  28. rep.th.source = th->dest;
  29. rep.th.doff = arg.iov[0].iov_len / 4;
  30. rep.th.seq = htonl(seq);
  31. rep.th.ack_seq = htonl(ack);
  32. rep.th.ack = 1;
  33. rep.th.window = htons(win);
  34. #ifdef CONFIG_TCP_MD5SIG
  35. if (key) {
  36. int offset = (ts) ? 3 : 0;
  37. rep.opt[offset++] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
  38. (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
  39. arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
  40. rep.th.doff = arg.iov[0].iov_len / 4;
  41. tcp_v4_md5_hash_addr((__u8 *) &rep.opt[offset], key, ip_hdr(skb)->saddr,
  42. ip_hdr(skb)->daddr, &rep.th);
  43. }
  44. #endif
  45. arg.flags = reply_flags;
  46. arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr,
  47. arg.iov[0].iov_len, IPPROTO_TCP, 0); /* 累加伪首部 */
  48. arg.csumoffset = offsetof(struct tcphdr, check) / 2;
  49. if (oif)
  50. arg.bound_dev_if = oif;
  51. arg.tos = tos;
  52. /* 调用IP层函数,发送此ACK段 */
  53. ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
  54. TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
  55. }
  1. struct ip_reply_arg {
  2. struct kvec iov[1];
  3. int flags;
  4. __wsum csum;
  5. int csumoffset; /* u16 offset of csum in iov[0].iov_base */
  6. int bound_dev_if;
  7. u8 tos;
  8. };
  9. struct kvec {
  10. void *iov_base;
  11. size_t iov_len;
  12. };

(3) 发送RST段

检测到对端异常时,发送RST段。

  1. static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
  2. {
  3. const struct tcphdr *th = tcp_hdr(skb);
  4. struct {
  5. struct tcphdr th;
  6. #ifdef CONFIG_TCP_MD5SIG
  7. __be32 opt[(TCPOLEN_MD5SIG_ALIGNED >> 2) ];
  8. #endif
  9. } rep;
  10. struct ip_reply_arg arg; /* 数据报的控制信息 */
  11. #ifdef CONFIG_TCP_MD5SIG
  12. struct tcp_md5sig_key *key;
  13. const __u8 *hash_location = NULL;
  14. unsigned char newhash[16];
  15. int genhash;
  16. struct sock *sk1 = NULL;
  17. #endif
  18. struct net *net;
  19. /* Never send a reset in response to a reset. */
  20. if (th->rst)
  21. return;
  22. if (skb_rtable(skb)->rt_type != RTN_LOCAL)
  23. return;
  24. /* Swap the send and the receive. */
  25. memset(&rep, 0, sizeof(rep));
  26. rep.th.dest = th->source;
  27. rep.th.source = th->dest;
  28. rep.th.doff = sizeof(struct tcphdr) / 4;
  29. rep.th.rst = 1;
  30. if (th->ack) {
  31. rep.th.seq = th->ack_seq;
  32. } else {
  33. rep.th.ack = 1;
  34. rep.th.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2));
  35. }
  36. memset(&arg, 0, sizeof(arg));
  37. arg.iov[0].iov_base = (unsigned char *) &rep;
  38. arg.iov[0].iov_len = sizeof(rep.th);
  39. #ifdef CONFIG_TCP_MD5SIG
  40. /* 此处省略MD5选项的处理 */
  41. ...
  42. #endif
  43. arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr,
  44. arg.iov[0].iov_len, IPPROTO_TCP, 0);
  45. arg.csumoffset = offsetof(struct tcphdr, check) / 2;
  46. arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
  47. /* When socket is gone, all binding information is lost.
  48. * routing might fail in this case. No choice here, if we choose to force input interface,
  49. * we will misroute in case of asymmetric route.
  50. */
  51. if (sk)
  52. arg.bound_dev_if = sk->sk_bound_dev_if;
  53. net = dev_net(skb_dst(skb)->dev);
  54. arg.tos = ip_hdr(skb)->tos;
  55. /* 调用IP层函数发送 */
  56. ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
  57. TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
  58. TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
  59. #ifdef CONFIG_TCP_MD5SIG
  60. /* 省略MD5处理 */
  61. ...
  62. #endif
  63. }

(4) 析构函数

释放request_sock实例前调用。

  1. /* IPv4 request_sock destructor. */
  2. static void tcp_v4_reqsk_destructor(struct request_sock *req)
  3. {
  4. kfree(inet_rsk(req)->opt); /* 释放IP选项实例 */
  5. }

(5) 超时处理函数

不是真正的SYNACK超时处理函数,简单更新下统计变量。

  1. void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req)
  2. {
  3. NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEOUTS);
  4. }

TCP连接建立系列 — 服务端接收ACK段(一)的更多相关文章

  1. TCP连接建立系列 — 服务端接收ACK段(二)

    本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 创建新sock 协议族相关的操作函数,我们要看的是TCP ...

  2. TCP连接建立系列 — 服务端接收SYN段

    本文主要分析:服务器端接收到SYN包时的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 接收入口 1. 状态为ESTABLISHED时,用tcp_rcv_esta ...

  3. TCP连接建立系列 — 服务端发送SYNACK段

    本文主要分析:服务器端如何构造和发送SYNACK段. 内核版本:3.6 Author:zhangskd @ csdn blog 发送入口 tcp_v4_send_synack()用于发送SYNACK段 ...

  4. tcp syn-synack-ack 服务端接收ack

    TCP 服务端 接收到ack tcp_v4_rcv() -> tcp_v4_do_rcv() -> tcp_v4_hnd_req() + tcp_child_process()tcp_v4 ...

  5. C# TCP socket发送大数据包时,接收端和发送端数据不一致 服务端接收Receive不完全

    简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...

  6. linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现

    1 TCP简介 tcp是一种基于流的应用层协议,其“可靠的数据传输”实现的原理就是,“拥塞控制”的滑动窗口机制,该机制包含的算法主要有“慢启动”,“拥塞避免”,“快速重传”. 2 TCP socket ...

  7. QTcpSocket-Qt使用Tcp通讯实现服务端和客户端

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QTcpSocket-Qt使用Tcp通讯实现服务端和客户端     本文地址:https:// ...

  8. 前端学习 node 快速入门 系列 —— 服务端渲染

    其他章节请看: 前端学习 node 快速入门 系列 服务端渲染 在简易版 Apache一文中,我们用 node 做了一个简单的服务器,能提供静态资源访问的能力. 对于真正的网站,页面中的数据应该来自服 ...

  9. TCP连接建立系列 — 客户端接收SYNACK和发送ACK

    主要内容:客户端接收SYNACK.发送ACK,完成连接的建立. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 接收入口 tcp_v4_rcv |--&g ...

随机推荐

  1. org.hibernate.NonUniqueObjectException: a different object with the same identifier value was alread---------程序报错

    今天遇到了这个问题: org.hibernate.NonUniqueObjectException: a different object with the same identifier value ...

  2. java集合-HashMap

    HashMap基于哈希表的 Map 接口的实现,以 key-value 的形式存在.在 HashMap 中,key-value 总是会当做一个整体来处理,系统会根据 hash 算法来来计算 key-v ...

  3. [Android] 建立与使用Library

    [Android] 建立与使用Library 前言 使用Eclipse开发Android项目时,开发人员可以将可重用的程序代码,封装为Library来提供其他开发人员使用.本篇文章介绍如何将可重用的程 ...

  4. 编写高性能Javascript代码的若干建议

    多年来,Javascript一直在web应用开发中占据重要的地位,但是很多开发者往往忽视一些性能方面的知识,特别是随着计算机硬件的不断升级,开发者越发觉得Javascript性能优化的好不好对网页的执 ...

  5. Web 开发最有用的50款 jQuery 插件集锦——《综合篇》

    这篇文章是<Web 开发最有用的50款 jQuery 插件集锦>系列的最后一篇,整个系列向大家分享了在网站开发中非常有帮助的 50 款 jQuery 插件,这些插件按用途主要有以下类别:网 ...

  6. 灵感来自 Google & YouTube 的苗条的进度栏效果

    NProgress.js 是纳米级的进度条插件.拥有逼真的的涓涓细流动画效果来告诉你的用户,某些事情正在发生.它的灵感来自于谷歌,YouTube,应用了,这款苗条的进度条是完美的,适用于 Turbol ...

  7. angularjs封装bootstrap官网的时间插件datetimepicker

    背景:angular与jquery类库的协作 第三方类库中,不得不提的是大名鼎鼎的jquery,现在基本上已经是国内web开发的必修工具了.它灵活的dom操作,让很多web开发人员欲罢不能.再加上已经 ...

  8. LigerUi框架+jquery+ajax无刷新留言板系统的实现

    前些天发布了LigerUi框架的增.删.改代码,一堆代码真的也没一张图片.有的网友推荐上图,所有今天把涉及到这个框架的开源的留言板共享给大家.在修改的过程中可能有些不足的地方希望大家拍砖. 因为留言板 ...

  9. Request.MapPath和ServerMapPath

    一.路径 / 念 反斜杠,/ 是超文本协议的路径分隔符号,所有的网站在浏览器中显示的路径分隔都是以"/"表示.它一般代表虚拟路径. \ 念 斜杠,在普通程序代码中则以"\ ...

  10. CSS基础教程 -- 媒体查询屏幕适配

    响应式布局 Media Query 的使用方法 在上例中, 我们使用Media Queries来根据3种不同尺寸的窗口使用3种不同的样式.通过不同的媒体类型和条件定义样式表规则,媒体查询让CSS可以更 ...