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. Java检测对象是否相等

    关系运算符==和!=也适用于所有对象,但它们的含义通常会使初涉Java 领域的人找不到北.下面是一个例子: public class Equivalence { public static void ...

  2. mysql 5.7.15 vs mysql 5.6.31性能测试以及不同linux内核性能比较

    最近,将部分开发和测试环境的mysql升级到5.7之后,今天抽时间测试了下5.6和5.7 PK查询的性能,使用mysqlslap进行测试,测试结果发现在低配下,percona 5.6.31大约比5.7 ...

  3. 免费的 Photoshop Apple Watch 原型设计素材

    大量的扁平化的苹果设备原型展示了响应式的 Web 设计.这是一组免费的 Photoshop Apple Watch 原型 PSD 设计素材,文件包括 iPhone.iPad. iMac 和 Macbo ...

  4. AloneJs —— 简洁高效的JavaScript UI库

    以前做项目时用了一些第三方的JS UI库,项目比较low的时候用还行,一旦项目要求比较高,特别是交互比较复杂时,某些第三方UI库就显得无能为力,用起来也不顺手,改也不好改,所以我就自己基于jQuery ...

  5. Wifite.py 修正版脚本代码

    Kali2.0系统自带的WiFite脚本代码中有几行错误,以下是修正后的代码: #!/usr/bin/python # -*- coding: utf-8 -*- """ ...

  6. myeclipse 2015 CI 16发布【附下载】

    2015升级版再次来袭! 更新日志: Slack Integration 新版本集成了Slack,你只需要注册一个Slack帐号然后就可以发送和接收代码片段.你甚至不需要登录Slack就可以直接在Ec ...

  7. Javascript 中的window.parent ,window.top,window.self 详解

    在应用有frameset或者iframe的页面时,parent是父窗口,top是最顶级父窗口(有的窗口中套了好几层frameset或者iframe),self是当前窗口, opener是用open方法 ...

  8. JavaScript学习12 JS中定义对象的几种方式

    JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...

  9. iOS开发之──传感器使用 (转载)

    在实际的应用开发中,会用到传感器,下面首先介绍一下iphone4的传感器,然后对一些传感器的开发的API作一简单介绍. AD:WOT2015 互联网运维与开发者大会 热销抢票 在实际的应用开发中,会用 ...

  10. Android 手机卫士--实现设置界面的一个条目布局结构

    本文地址:http://www.cnblogs.com/wuyudong/p/5908986.html,转载请注明源地址. 本文以及后续文章,将一步步完善功能列表: 要点击九宫格中的条目,需要注册点击 ...