深入理解TCP协议:三次握手详解

1.什么是三次握手?

TCP协议建立连接时,需要三次发送数据包:

第一次:客户机向服务器端请求建立连接

第二次:服务器收到客户机的请求,发出响应

第三次:客户机收到响应 认为连接建立成功

详细过程:

深入理解TCP协议:三次握手详解

名词解释:

SYN - 标志位 只有第一次和第二次为1,第三次和其他任何情况都是0

ACK - 标志位 只有第一次不为1,第二,三次和其他任何情况都是1

Sequence Number 顺序号,初始值为随机数

Acknowledgment Number 确认号,下一次对收到的数据顺序号的期望

第一次:

客户机 >>服务器

SYN =1

ACK =0

Sequence Number=X(随机数)

第二次:

SYN =1

ACK =1

Sequence Number=Y(随机数)

Acknowledgment Number=X+1

客户机<<服务器

第三次:

SYN =0

ACK =1

Sequence Number=X+1

Acknowledgment Number=Y+1

客户机 >>服务器

2.为什么要有三次握手?

我们考虑一次和两次握手为什么不行:

考虑

链路1 客户机 >>服务器

链路2 客户机<<服务器

一次握手:

如果链路1故障,客户机仍然会认为连接成功,而服务器不知道连接发生

如果链路2故障,服务器和客户机都会认为连接成功

两次握手:

如果链路2故障,服务器会认为连接成功

接下来考虑三次握手:

如果链路1故障

服务器端收不到第一次握手包,因而不会认为有连接请求,没有误认为连接成功,也不会发送第二次握手包

客户机收不到第二次握手包(服务器端没有发送),没有误认为连接成功

双方都不会误认为连接成功

如果链路2故障

客户机收不到第二次握手包(服务器端发送了但由于链路故障没有收到),没有误认为连接成功,也不会发送第三次握手包

服务器端收不到第三次握手包(客户机没有发送),没有误认为连接成功

双方都不会误认为连接成功

3.捕获一次典型的TCP三次握手:

我们使用wireshark工具

首先 在cmd执行 ping www.baidu.com

深入理解TCP协议:三次握手详解

这是为了确定目标IP地址,便于设置捕获规则

ip.addr==180.101.49.12

打开www.baidu.com

捕获到深入理解TCP协议:三次握手详解

这三个数据包即是TCP三次握手数据包(192.168.3.89是本地IP)

这里有一个问题:为什么Seq的初始值是0而不是一个随机数?

这是wireShark软件本身的特性,显示的不是实际值而是相对值

深入理解TCP协议:三次握手详解

三次Sequence Number和Acknowledgment Number的真实值(使用十六进制):

第一次(分别是前八位和后八位):

深入理解TCP协议:三次握手详解

第二次:

深入理解TCP协议:三次握手详解

第三次:

深入理解TCP协议:三次握手详解

可以看到符合

X 0

Y X+1

X+1 Y+1的规律

和我们的预期相符

三次数据包的标志位:

第一次:

深入理解TCP协议:三次握手详解

第二次:

深入理解TCP协议:三次握手详解

第三次:

深入理解TCP协议:三次握手详解

可以看到wireshark已经非常贴心的替我们做好了标注

也和我们的预期一致

3.代码追踪和分析:

在之前的实验中,我们知道发出TCP连接请求的函数是__sys_connect

我们分析这个函数的源代码

 1 int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
 2 {
 3     struct socket *sock;
 4     struct sockaddr_storage address;
 5     int err, fput_needed;
 6     sock = sockfd_lookup_light(fd, &err, &fput_needed);
 7     if (!sock)
 8         goto out;
 9     err = move_addr_to_kernel(uservaddr, addrlen, &address);
10     if (err < 0)
11         goto out_put;
12     err =
13         security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
14     if (err)
15         goto out_put;
16  
17     err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
18                  sock->file->f_flags);
19 out_put:
20     fput_light(sock->file, fput_needed);
21 out:
22     return err;
23 }

主要的执行过程是

1 err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
2                     sock->file->f_flags)

这是一个函数指针,我们通过gdb,发现指向:inet_stream_connect

源代码

 1 int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
 2             int addr_len, int flags)
 3 {
 4     int err;
 5 
 6     lock_sock(sock->sk);
 7     err = __inet_stream_connect(sock, uaddr, addr_len, flags, 0);
 8     release_sock(sock->sk);
 9     return err;
10 }

发现是对__inet_stream_connect的封装,前面应当是并发控制

继续追踪源代码:

 1 int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
 2               int addr_len, int flags)
 3 {
 4     struct sock *sk = sock->sk;
 5     int err;
 6     long timeo;
 7 
 8     if (addr_len < sizeof(uaddr->sa_family))
 9         return -EINVAL;
10 
11     if (uaddr->sa_family == AF_UNSPEC) {
12         err = sk->sk_prot->disconnect(sk, flags);
13         sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
14         goto out;
15     }
16     switch (sock->state) {
17     default:
18         err = -EINVAL;
19         goto out;
20     case SS_CONNECTED:
21         err = -EISCONN;
22         goto out;
23     case SS_CONNECTING:
24         err = -EALREADY;
25         break;
26     case SS_UNCONNECTED:
27         err = -EISCONN;
28         if (sk->sk_state != TCP_CLOSE)
29             goto out;
30         err = sk->sk_prot->connect(sk, uaddr, addr_len);
31 ...太长了 后面的先省略

 重点是err = sk->sk_prot->connect(sk, uaddr, addr_len);


可以看到这个函数又是通过一个函数指针工作的 

err = sk->sk_prot->connect(sk, uaddr, addr_len);

追踪这个函数指针,发现最终指向:tcp_v4_connect

源代码

  1 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
  2 {
  3     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
  4     struct inet_sock *inet = inet_sk(sk);
  5     struct tcp_sock *tp = tcp_sk(sk);
  6     __be16 orig_sport, orig_dport;
  7     __be32 daddr, nexthop;
  8     struct flowi4 *fl4;
  9     struct rtable *rt;
 10     int err;
 11     struct ip_options_rcu *inet_opt;
 12     struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row;
 13  
 14     if (addr_len < sizeof(struct sockaddr_in))
 15         return -EINVAL;
 16  
 17     if (usin->sin_family != AF_INET)
 18         return -EAFNOSUPPORT;
 19  
 20     nexthop = daddr = usin->sin_addr.s_addr;
 21     inet_opt = rcu_dereference_protected(inet->inet_opt,
 22                          lockdep_sock_is_held(sk));
 23     if (inet_opt && inet_opt->opt.srr) {
 24         if (!daddr)
 25             return -EINVAL;
 26         nexthop = inet_opt->opt.faddr;
 27     }
 28  
 29     orig_sport = inet->inet_sport;
 30     orig_dport = usin->sin_port;
 31     fl4 = &inet->cork.fl.u.ip4;
 32     rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,
 33                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
 34                   IPPROTO_TCP,
 35                   orig_sport, orig_dport, sk);
 36     if (IS_ERR(rt)) {
 37         err = PTR_ERR(rt);
 38         if (err == -ENETUNREACH)
 39             IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
 40         return err;
 41     }
 42  
 43     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
 44         ip_rt_put(rt);
 45         return -ENETUNREACH;
 46     }
 47  
 48     if (!inet_opt || !inet_opt->opt.srr)
 49         daddr = fl4->daddr;
 50  
 51     if (!inet->inet_saddr)
 52         inet->inet_saddr = fl4->saddr;
 53     sk_rcv_saddr_set(sk, inet->inet_saddr);
 54  
 55     if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {
 56         /* Reset inherited state */
 57         tp->rx_opt.ts_recent       = 0;
 58         tp->rx_opt.ts_recent_stamp = 0;
 59         if (likely(!tp->repair))
 60             tp->write_seq       = 0;
 61     }
 62  
 63     inet->inet_dport = usin->sin_port;
 64     sk_daddr_set(sk, daddr);
 65  
 66     inet_csk(sk)->icsk_ext_hdr_len = 0;
 67     if (inet_opt)
 68         inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
 69  
 70     tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;
 71  
 72     tcp_set_state(sk, TCP_SYN_SENT);
 73     err = inet_hash_connect(tcp_death_row, sk);
 74     if (err)
 75         goto failure;
 76  
 77     sk_set_txhash(sk);
 78  
 79     rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,
 80                    inet->inet_sport, inet->inet_dport, sk);
 81     if (IS_ERR(rt)) {
 82         err = PTR_ERR(rt);
 83         rt = NULL;
 84         goto failure;
 85     }
 86  
 87     sk->sk_gso_type = SKB_GSO_TCPV4;
 88     sk_setup_caps(sk, &rt->dst);
 89     rt = NULL;
 90  
 91     if (likely(!tp->repair)) {
 92         if (!tp->write_seq)
 93             tp->write_seq = secure_tcp_seq(inet->inet_saddr,
 94                                inet->inet_daddr,
 95                                inet->inet_sport,
 96                                usin->sin_port);
 97         tp->tsoffset = secure_tcp_ts_off(sock_net(sk),
 98                          inet->inet_saddr,
 99                          inet->inet_daddr);
100     }
101  
102     inet->inet_id = tp->write_seq ^ jiffies;
103  
104     if (tcp_fastopen_defer_connect(sk, &err))
105         return err;
106     if (err)
107         goto failure;
108  
109     err = tcp_connect(sk);
110  
111     if (err)
112         goto failure;
113  
114     return 0;
115  
116 failure:
117  
118     tcp_set_state(sk, TCP_CLOSE);
119     ip_rt_put(rt);
120     sk->sk_route_caps = 0;
121     inet->inet_dport = 0;
122     return err;
123 }
上一篇:TCP协议的初始化及socket创建TCP套接字描述符


下一篇:LeetCode刷题笔记 Java 腾讯 数组字符串 有效的括号