深入理解TCP协议及其源代码

TCP三次握手理论

三次握手过程

深入理解TCP协议及其源代码

第一次握手

客户端A向服务端B发出连接请求,同步位SYN=1,初始序列seq=x,连接请求报文段不能携带数据,但是要消耗一个序号,这时客户端A进入SYN-SENT(同步已发送状态)

第二次握手

服务端B收到请求报文段之后,向A发送后确认。将同部位SYN和确认位都置为1,确认序号ack=x+1,同时自己选择一个初始序号seq=y。连接接收报文也不能携带数据,但是也要消耗一个序号,这时服务端进入SYN-RCVD(同步收到状态)

第三次握手

A收到B的确认时候要给B一个确认。确认报文段的确认位ACK=1,确认号ack=y+1,自己的序号seq=x+1。这时,TCP连接已经建立,客户端进入ESTABLISHED(已建立连接状态)。B收到A发出的确认报文之后也进入已建立连接状态

状态转换

在TCP连接建立的过程中会涉及到状态的转换,下面以有限状态机的形式给出在连接建立、数据传送和连接终止时所发生事件之间是如何转换的。

深入理解TCP协议及其源代码

三次握手源代码分析

经过前一次实验,我们调研了socket相关的系统调用,了解了在服务器端与客户端socket建立、连接、通信以及终止过程中所涉及到的系统调用。

其中,在服务器端相继调用__sys_socket__sys_bind__sys_listen内核函数后,服务器进入监听状态,即打开端口被动地等待客户端连接。当客户端调用__sys_connect发出连接时,这就是我们今天所探讨地tcp三次握手过程的开始了。

那么,在TCP三次握手这个过程中所涉及到的调用过程是怎样的呢?

TCP过程分析

经过前一次实验,我们调研了socket相关的系统调用,了解了在服务器端与客户端socket建立、连接、通信以及终止过程中所涉及到的系统调用。

其中,在服务器端相继调用__sys_socket__sys_bind__sys_listen内核函数后,服务器进入监听状态,即打开端口被动地等待客户端连接。当客户端调用__sys_connect发出连接时,这就是我们今天所探讨地tcp三次握手过程的开始了。

那么,在TCP三次握手这个过程中所涉及到的调用过程是怎样的呢?

深入理解TCP协议及其源代码

 

 

三次握手主要目的:

信息对等和防止超时。防止超时导致脏连接。如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

滑动窗口

TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。TCP使用肯定确认技术,其确认号指的是下一个所期待的字节。 假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送序列号为1、2、3的三个数据包,接收方设备成功接收数据包,用序列号4确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。当链路变好了或者变差了这个窗口还会发生变话,并不是第一次协商好了以后就永远不变了。

滑动窗口协议,是TCP使用的一种流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。

流量控制:端到端,接收端的应用层处理速度决定和网速无关,由接收端返回的rwnd控制

cwnd:发送端窗口( congestion window )
rwnd:接收端窗口(receiver window)

拥塞控制

拥塞控制: 发送端主动控制cwnd,有慢启动(从cwnd初始为1开始启动,指数启动),拥塞避免(到达ssthresh后,为了避免拥塞开始尝试线性增长),快重传(接收方每收到一个报文段都要回复一个当前最大连续位置的确认,发送方只要一连收到三个重复确认就知道接收方丢包了,快速重传丢包的报文,并TCP马上把拥塞窗口 cwnd 减小到1),快恢复(直接从ssthresh线性增长)。

如果网络上的延时突然增加,那么TCP对这个事作出的应对只有重传数据,但是重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,于是这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么行事,那么马上就会形成“网络风暴”,TCP这个协议就会拖垮整个网络。所以TCP不能忽略网络上发生的事情,而无脑地一个劲地重发数据,对网络造成更大的伤害。对此TCP的设计理念是:TCP不是一个自私的协议,当拥塞发生的时候,要做自我牺牲。就像交通阻塞一样,每个车都应该把路让出来,而不要再去抢路了。

慢启动

只有在TCP连接建立和网络出现超时时才使用。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。另外,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。

为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量(如何设置ssthresh)。慢开始门限ssthresh的用法如下:

当 cwnd < ssthresh 时,使用上述的慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
拥塞避免算法:让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

与快重传配合使用的还有快恢复算法,其过程有以下两个要点:

当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢启动门限ssthresh减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。
由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
上图给出了快重传和快恢复的示意图,并标明了“TCP Reno版本”。区别:新的 TCP Reno 版本在快重传之后采用快恢复算法而不是采用慢开始算法。

发送方窗口的上限值 = Min [ rwnd, cwnd ]

当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。
差错控制
TCP使用差错控制来提供可靠性。差错控制包括以下的一些机制:检测和重传受到损伤的报文段、重传丢失的报文段、保存失序到达的报文段直至缺失的报文到期,以及检测和丢弃重复的报文段。TCP通过三个简单的工具来完成其差错控制:检验和、确认以及超时。

源码分析

结构体变量struct proto tcp_prot指定了TCP协议栈的访问接口函数

深入理解TCP协议及其源代码
 1 struct proto tcp_prot = {
 2     .name                     = "TCP",
 3     .owner                    = THIS_MODULE,
 4     .close                    = tcp_close,
 5     .pre_connect              = tcp_v4_pre_connect,
 6     .connect                  = tcp_v4_connect,
 7     .disconnect               = tcp_disconnect,
 8     .accept                   = inet_csk_accept,
 9     .ioctl                    = tcp_ioctl,
10     .init                     = tcp_v4_init_sock,
11     .destroy                  = tcp_v4_destroy_sock,
12     .shutdown                 = tcp_shutdown,
13     .setsockopt               = tcp_setsockopt,
14     .getsockopt               = tcp_getsockopt,
15     .keepalive                = tcp_set_keepalive,
16     .recvmsg                  = tcp_recvmsg,
17     .sendmsg                  = tcp_sendmsg,
18     .sendpage                 = tcp_sendpage,
19     .backlog_rcv              = tcp_v4_do_rcv,
20     .release_cb               = tcp_release_cb,
21     .hash                     = inet_hash,
22     .unhash                   = inet_unhash,
23     .get_port                 = inet_csk_get_port,
24     .enter_memory_pressure    = tcp_enter_memory_pressure,
25     .leave_memory_pressure    = tcp_leave_memory_pressure,
26     .stream_memory_free       = tcp_stream_memory_free,
27     .sockets_allocated        = &tcp_sockets_allocated,
28     .orphan_count             = &tcp_orphan_count,
29     .memory_allocated         = &tcp_memory_allocated,
30     .memory_pressure          = &tcp_memory_pressure,
31     .sysctl_mem               = sysctl_tcp_mem,
32     .sysctl_wmem_offset       = offsetof(struct net, ipv4.sysctl_tcp_wmem),
33     .sysctl_rmem_offset       = offsetof(struct net, ipv4.sysctl_tcp_rmem),
34     .max_header               = MAX_TCP_HEADER,
35     .obj_size                 = sizeof(struct tcp_sock),
36     .slab_flags               = SLAB_TYPESAFE_BY_RCU,
37     .twsk_prot                = &tcp_timewait_sock_ops,
38     .rsk_prot                 = &tcp_request_sock_ops,
39     .h.hashinfo               = &tcp_hashinfo,
40     .no_autobind              = true,
深入理解TCP协议及其源代码

2.1 首先客户端发送SYN报文

  tcp_v4_connect函数

深入理解TCP协议及其源代码

深入理解TCP协议及其源代码
  1 /* This will initiate an outgoing connection. */
  2 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
  3 {
  4     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
  5     struct inet_sock *inet = inet_sk(sk);
  6     struct tcp_sock *tp = tcp_sk(sk);
  7     __be16 orig_sport, orig_dport;
  8     __be32 daddr, nexthop;
  9     struct flowi4 *fl4;
 10     struct rtable *rt;
 11     int err;
 12     struct ip_options_rcu *inet_opt;
 13     struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row;
 14 
 15     if (addr_len < sizeof(struct sockaddr_in))
 16         return -EINVAL;
 17 
 18     if (usin->sin_family != AF_INET)
 19         return -EAFNOSUPPORT;
 20 
 21     nexthop = daddr = usin->sin_addr.s_addr;
 22     inet_opt = rcu_dereference_protected(inet->inet_opt,
 23                          lockdep_sock_is_held(sk));
 24     if (inet_opt && inet_opt->opt.srr) {
 25         if (!daddr)
 26             return -EINVAL;
 27         nexthop = inet_opt->opt.faddr;
 28     }
 29 
 30     orig_sport = inet->inet_sport;
 31     orig_dport = usin->sin_port;
 32     fl4 = &inet->cork.fl.u.ip4;
 33     rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,
 34                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
 35                   IPPROTO_TCP,
 36                   orig_sport, orig_dport, sk);
 37     if (IS_ERR(rt)) {
 38         err = PTR_ERR(rt);
 39         if (err == -ENETUNREACH)
 40             IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
 41         return err;
 42     }
 43 
 44     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
 45         ip_rt_put(rt);
 46         return -ENETUNREACH;
 47     }
 48 
 49     if (!inet_opt || !inet_opt->opt.srr)
 50         daddr = fl4->daddr;
 51 
 52     if (!inet->inet_saddr)
 53         inet->inet_saddr = fl4->saddr;
 54     sk_rcv_saddr_set(sk, inet->inet_saddr);
 55 
 56     if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {
 57         /* Reset inherited state */
 58         tp->rx_opt.ts_recent       = 0;
 59         tp->rx_opt.ts_recent_stamp = 0;
 60         if (likely(!tp->repair))
 61             tp->write_seq       = 0;
 62     }
 63 
 64     inet->inet_dport = usin->sin_port;
 65     sk_daddr_set(sk, daddr);
 66 
 67     inet_csk(sk)->icsk_ext_hdr_len = 0;
 68     if (inet_opt)
 69         inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
 70 
 71     tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;
 72 
 73     /* Socket identity is still unknown (sport may be zero).
 74      * However we set state to SYN-SENT and not releasing socket
 75      * lock select source port, enter ourselves into the hash tables and
 76      * complete initialization after this.
 77      */
 78     tcp_set_state(sk, TCP_SYN_SENT);
 79     err = inet_hash_connect(tcp_death_row, sk);
 80     if (err)
 81         goto failure;
 82 
 83     sk_set_txhash(sk);
 84 
 85     rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,
 86                    inet->inet_sport, inet->inet_dport, sk);
 87     if (IS_ERR(rt)) {
 88         err = PTR_ERR(rt);
 89         rt = NULL;
 90         goto failure;
 91     }
 92     /* OK, now commit destination to socket.  */
 93     sk->sk_gso_type = SKB_GSO_TCPV4;
 94     sk_setup_caps(sk, &rt->dst);
 95     rt = NULL;
 96 
 97     if (likely(!tp->repair)) {
 98         if (!tp->write_seq)
 99             tp->write_seq = secure_tcp_seq(inet->inet_saddr,
100                                inet->inet_daddr,
101                                inet->inet_sport,
102                                usin->sin_port);
103         tp->tsoffset = secure_tcp_ts_off(sock_net(sk),
104                          inet->inet_saddr,
105                          inet->inet_daddr);
106     }
107 
108     inet->inet_id = tp->write_seq ^ jiffies;
109 
110     if (tcp_fastopen_defer_connect(sk, &err))
111         return err;
112     if (err)
113         goto failure;
114 
115     err = tcp_connect(sk);
116 
117     if (err)
118         goto failure;
119 
120     return 0;
121 
122 failure:
123     /*
124      * This unhashes the socket and releases the local port,
125      * if necessary.
126      */
127     tcp_set_state(sk, TCP_CLOSE);
128     ip_rt_put(rt);
129     sk->sk_route_caps = 0;
130     inet->inet_dport = 0;
131     return err;
132 }
深入理解TCP协议及其源代码

2.2 另一头服务端accept等待连接请求

  inet_csk_accept函数

深入理解TCP协议及其源代码

深入理解TCP协议及其源代码
 1 /*
 2  * This will accept the next outstanding connection.
 3  */
 4 struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
 5 {
 6     struct inet_connection_sock *icsk = inet_csk(sk);
 7     struct request_sock_queue *queue = &icsk->icsk_accept_queue;
 8     struct request_sock *req;
 9     struct sock *newsk;
10     int error;
11 
12     lock_sock(sk);
13 
14     /* We need to make sure that this socket is listening,
15      * and that it has something pending.
16      */
17     error = -EINVAL;
18     if (sk->sk_state != TCP_LISTEN)
19         goto out_err;
20 
21     /* Find already established connection */
22     if (reqsk_queue_empty(queue)) {
23         long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
24 
25         /* If this is a non blocking socket don't sleep */
26         error = -EAGAIN;
27         if (!timeo)
28             goto out_err;
29 
30         error = inet_csk_wait_for_connect(sk, timeo);
31         if (error)
32             goto out_err;
33     }
34     req = reqsk_queue_remove(queue, sk);
35     newsk = req->sk;
36 
37     if (sk->sk_protocol == IPPROTO_TCP &&
38         tcp_rsk(req)->tfo_listener) {
39         spin_lock_bh(&queue->fastopenq.lock);
40         if (tcp_rsk(req)->tfo_listener) {
41             /* We are still waiting for the final ACK from 3WHS
42              * so can't free req now. Instead, we set req->sk to
43              * NULL to signify that the child socket is taken
44              * so reqsk_fastopen_remove() will free the req
45              * when 3WHS finishes (or is aborted).
46              */
47             req->sk = NULL;
48             req = NULL;
49         }
50         spin_unlock_bh(&queue->fastopenq.lock);
51     }
52 out:
53     release_sock(sk);
54     if (req)
55         reqsk_put(req);
56     return newsk;
57 out_err:
58     newsk = NULL;
59     req = NULL;
60 *err = error; 61 goto out; 62 }
深入理解TCP协议及其源代码

  inet_csk_wait_for_connect函数

深入理解TCP协议及其源代码
 1 /*
 2  * Wait for an incoming connection, avoid race conditions. This must be called
 3  * with the socket locked.
 4  */
 5 static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
 6 {
 7     struct inet_connection_sock *icsk = inet_csk(sk);
 8     DEFINE_WAIT(wait);
 9     int err;
10 
11     /*
12      * True wake-one mechanism for incoming connections: only
13      * one process gets woken up, not the 'whole herd'.
14      * Since we do not 'race & poll' for established sockets
15      * anymore, the common case will execute the loop only once.
16      *
17      * Subtle issue: "add_wait_queue_exclusive()" will be added
18      * after any current non-exclusive waiters, and we know that
19      * it will always _stay_ after any new non-exclusive waiters
20      * because all non-exclusive waiters are added at the
21      * beginning of the wait-queue. As such, it's ok to "drop"
22      * our exclusiveness temporarily when we get woken up without
23      * having to remove and re-insert us on the wait queue.
24      */
25     for (;;) {
26         prepare_to_wait_exclusive(sk_sleep(sk), &wait,
27                       TASK_INTERRUPTIBLE);
28         release_sock(sk);
29         if (reqsk_queue_empty(&icsk->icsk_accept_queue))
30             timeo = schedule_timeout(timeo);
31         sched_annotate_sleep();
32         lock_sock(sk);
33         err = 0;
34         if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
35             break;
36         err = -EINVAL;
37         if (sk->sk_state != TCP_LISTEN)
38             break;
39         err = sock_intr_errno(timeo);
40         if (signal_pending(current))
41             break;
42         err = -EAGAIN;
43         if (!timeo)
44             break;
45     }
46     finish_wait(sk_sleep(sk), &wait);
47     return err;
48 }
深入理解TCP协议及其源代码

2.3 三次握手中携带SYN/ACK的TCP头数据的发送和接收

  TCP/IP协议栈初始化

  inet_init函数

深入理解TCP协议及其源代码
 1 static __net_init int inet_init_net(struct net *net)
 2 {
 3     /*
 4      * Set defaults for local port range
 5      */
 6     seqlock_init(&net->ipv4.ip_local_ports.lock);
 7     net->ipv4.ip_local_ports.range[0] =  32768;
 8     net->ipv4.ip_local_ports.range[1] =  60999;
 9 
10     seqlock_init(&net->ipv4.ping_group_range.lock);
11     /*
12      * Sane defaults - nobody may create ping sockets.
13      * Boot scripts should set this to distro-specific group.
14      */
15     net->ipv4.ping_group_range.range[0] = make_kgid(&init_user_ns, 1);
16     net->ipv4.ping_group_range.range[1] = make_kgid(&init_user_ns, 0);
17 
18     /* Default values for sysctl-controlled parameters.
19      * We set them here, in case sysctl is not compiled.
20      */
21     net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
22     net->ipv4.sysctl_ip_fwd_update_priority = 1;
23     net->ipv4.sysctl_ip_dynaddr = 0;
24     net->ipv4.sysctl_ip_early_demux = 1;
25     net->ipv4.sysctl_udp_early_demux = 1;
26     net->ipv4.sysctl_tcp_early_demux = 1;
27 #ifdef CONFIG_SYSCTL
28     net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
29 #endif
30 
31     /* Some igmp sysctl, whose values are always used */
32     net->ipv4.sysctl_igmp_max_memberships = 20;
33     net->ipv4.sysctl_igmp_max_msf = 10;
34     /* IGMP reports for link-local multicast groups are enabled by default */
35     net->ipv4.sysctl_igmp_llm_reports = 1;
36     net->ipv4.sysctl_igmp_qrv = 2;
37 
38     return 0;
39 }
深入理解TCP协议及其源代码

 2.4 服务端接收客户端发来的SYN,发送SYN+ACK

  tcp_v4_do_rcv函数

深入理解TCP协议及其源代码
 1 * The socket must have it's spinlock held when we get
 2  * here, unless it is a TCP_LISTEN socket.
 3  *
 4  * We have a potential double-lock case here, so even when
 5  * doing backlog processing we use the BH locking scheme.
 6  * This is because we cannot sleep with the original spinlock
 7  * held.
 8  */
 9 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
10 {
11     struct sock *rsk;
12 
13     if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
14         struct dst_entry *dst = sk->sk_rx_dst;
15 
16         sock_rps_save_rxhash(sk, skb);
17         sk_mark_napi_id(sk, skb);
18         if (dst) {
19             if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
20                 !dst->ops->check(dst, 0)) {
21                 dst_release(dst);
22                 sk->sk_rx_dst = NULL;
23             }
24         }
25         tcp_rcv_established(sk, skb);
26         return 0;
27     }
28 
29     if (tcp_checksum_complete(skb))
30         goto csum_err;
31 
32     if (sk->sk_state == TCP_LISTEN) {
33         struct sock *nsk = tcp_v4_cookie_check(sk, skb);
34 
35         if (!nsk)
36             goto discard;
37         if (nsk != sk) {
38             if (tcp_child_process(sk, nsk, skb)) {
39                 rsk = nsk;
40                 goto reset;
41             }
42             return 0;
43         }
44     } else
45         sock_rps_save_rxhash(sk, skb);
46 
47     if (tcp_rcv_state_process(sk, skb)) {
48         rsk = sk;
49         goto reset;
50     }
51     return 0;
52 
53 reset:
54     tcp_v4_send_reset(rsk, skb);
55 discard:
56     kfree_skb(skb);
57     /* Be careful here. If this function gets more complicated and
58      * gcc suffers from register pressure on the x86, sk (in %ebx)
59      * might be destroyed here. This current version compiles correctly,
60      * but you have been warned.
61      */
62     return 0;
63 
64 csum_err:
65     TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
66     TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
67     goto discard;
68 }
深入理解TCP协议及其源代码

2.5 客户端收到服务端的SYN+ACK,发送ACK

  tcp_rcv_synsent_state_proces函数

深入理解TCP协议及其源代码
1 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
2                                          const struct tcphdr *th, unsigned int len)
3 {    
4 ..
5            tcp_send_ack(sk);
6 ...
7 }
深入理解TCP协议及其源代码

  到这里我们已经从linux网络核心的角度从架构上整体理解了三次握手,即携带SYN/ACK标志的数据收发过程。

3、gdb调试过程

  深入理解TCP协议及其源代码

  深入理解TCP协议及其源代码

深入理解TCP协议及其源代码
(gdb) c
Continuing.

Breakpoint 1, __sys_socket (family=2, type=1, protocol=0) at net/socket.c:1346
1346        retval = sock_create(family, type, protocol, &sock);
(gdb) c
Continuing.

Breakpoint 2, __sys_accept4 (fd=4, upeer_sockaddr=0xffbb869c, 
    upeer_addrlen=0xffbb867c, flags=0) at net/socket.c:1542
1542    {
(gdb) c
Continuing.

Breakpoint 1, __sys_socket (family=2, type=1, protocol=0) at net/socket.c:1346
1346        retval = sock_create(family, type, protocol, &sock);
(gdb) c
Continuing.

Breakpoint 3, tcp_v4_connect (sk=0xffff888006498880, uaddr=0xffffc90000043e20, 
    addr_len=16) at net/ipv4/tcp_ipv4.c:203
203    {
(gdb) c
Continuing.

Breakpoint 4, tcp_v4_rcv (skb=0xffff8880068ed4e0) at net/ipv4/tcp_ipv4.c:1782
1782    {
(gdb) c
Continuing.

Breakpoint 4, tcp_v4_rcv (skb=0xffff888007584000) at net/ipv4/tcp_ipv4.c:1782
1782    {
(gdb) c
Continuing.

Breakpoint 4, tcp_v4_rcv (skb=0xffff888007584100) at net/ipv4/tcp_ipv4.c:1782
1782    {
(gdb) c
Continuing.

Breakpoint 4, tcp_v4_rcv (skb=0xffff8880068ed4e0) at net/ipv4/tcp_ipv4.c:1782
1782    {
(gdb) c
Continuing.

Breakpoint 4, tcp_v4_rcv (skb=0xffff888007584100) at net/ipv4/tcp_ipv4.c:1782
1782    {
(gdb) c
Continuing.

Breakpoint 2, __sys_accept4 (fd=4, upeer_sockaddr=0xffbb869c, 
    upeer_addrlen=0xffbb867c, flags=0) at net/socket.c:1542
1542    {
(gdb) 
上一篇:python,socket网络编程,最简单的server端和client端代码


下一篇:TCP连接建立及相关socket深度探析