TCP/IP工作流5 connect 2

tcp_v4_connect

tcp_v4_connect函数定义在tcp_ipv4.c中。这个函数到底会做些什么事情呢?从TCP/IP协议中,TCP连接建立的过程中,我们知道:TCP连接需要三次握手。
1. 客户端发送SYN包
2. 服务器端收SYN包,发SYN+ACK包
3. 客户端收SYN+ACK包,发ACK
为什么要理解为三次握手(handshake),而不叫三次碰面,文青一点的解释是,三次握手说明两个人已经见了三次面了,基本上可以算上认识了。那么TCP连接的建立也相当于是两台机器”认识“了。
按照协议的内容来理解的话,tcp_v4_connect返回0的时候,说明一条TCP连接已经建立了。

169 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
170 {
171     struct inet_sock *inet = inet_sk(sk);
172     struct tcp_sock *tp = tcp_sk(sk);
173     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
174     struct rtable *rt;
175     __be32 daddr, nexthop;
176     int tmp;
177     int err;
179     if (addr_len < sizeof(struct sockaddr_in))
180         return -EINVAL;
182     if (usin->sin_family != AF_INET)
183         return -EAFNOSUPPORT;
185     nexthop = daddr = usin->sin_addr.s_addr;
186     if (inet->opt && inet->opt->srr) {
187         if (!daddr)
188             return -EINVAL;
189         nexthop = inet->opt->faddr;
190     }
192     tmp = ip_route_connect(&rt, nexthop, inet->saddr,
193                    RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
194                    IPPROTO_TCP,
195                    inet->sport, usin->sin_port, sk, 1);
196     if (tmp < 0) {
197         if (tmp == -ENETUNREACH)
198             IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
199         return tmp;
200     }
202     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
203         ip_rt_put(rt);
204         return -ENETUNREACH;
205     }
207     if (!inet->opt || !inet->opt->srr)
208         daddr = rt->rt_dst;
210     if (!inet->saddr)
211         inet->saddr = rt->rt_src;
212     inet->rcv_saddr = inet->saddr;
214     if (tp->rx_opt.ts_recent_stamp && inet->daddr != daddr) {
216         tp->rx_opt.ts_recent       = 0;
217         tp->rx_opt.ts_recent_stamp = 0;
218         tp->write_seq          = 0;
219     }
221     if (tcp_death_row.sysctl_tw_recycle &&
222         !tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) {
223         struct inet_peer *peer = rt_get_peer(rt);
224         /*
225          * VJ's idea. We save last timestamp seen from
226          * the destination in peer table, when entering state
227          * TIME-WAIT * and initialize rx_opt.ts_recent from it,
228          * when trying new connection.
230         if (peer != NULL &&
231             peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {
232             tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
233             tp->rx_opt.ts_recent = peer->tcp_ts;
234         }
235     }
237     inet->dport = usin->sin_port;
238     inet->daddr = daddr;
240     inet_csk(sk)->icsk_ext_hdr_len = 0;
241     if (inet->opt)
242         inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
244     tp->rx_opt.mss_clamp = 536;

251     tcp_set_state(sk, TCP_SYN_SENT);

这同样是一个很长的函数。传入的参数不再说了,前面已经说过。
函数开头定义的变量中,inet为socket在INET层中的表示,tp是socket在TCP层中的表示。rt是路由表在内核中的表示。IPV4中我们用4个数字来表示IP地址,每个数字范围为0-255,所以可以用一个32位数字来表示IP地址,所以有daddr, nexthop的字义。
179-191 每个函数例行的参数检查。nexthop和daddr被初始化为传入的IP地址。
192 想要连接必须知道远方的服务器是否是活动状态的,调用ip_route_connect函数。
这个函数的调用关系过于复杂,在学习IP协议时再展开。返回的结果记录在rt指针中,也就是找到或者新建了一个路由表实例。
196-200 如果函数返回的非0,说明网络连接有问题,就不尝试了,记录相关的信息,返回不可达错误。

202-205 如果得到的路由表的标识是广播或者多播类型,那么也有问题。因为,TCP是一对一的连接。
207-208 这个不太明白。应该是如果inet中的IP选项没有设备,则设置目的地址为查找到的路由表的目的地址。这种情况可能出现在路由重定向中。
210-212 设置inet的源地址。如果没有初始化的话,就用rt中的源地址。设置接收的源地址为inet的源地址。
214-219 如果现在的tp中接收选项中的时间戳不为0,而且发生了重定向,则重置接收的时间戳。
221-235 根据内核的注释知道,这里是针对处于 TIME-WAIT状态的TCP连接,同时要启动新的连接,新的连接和当前的TCP连接具有相同的hash参数,即TCP连接中的4元组是相同的。如果是这样的话,新建立的连接要从即将关闭的连接那里,继承一些时间戳。
237-238 初始化inet中的目的商品和目的地址,注意到这里的区别,dport是usin->sin_port来初始化的,而inet->daddr是用daddr来初始化的。回头再看前面的代码,发现基本上都是针对daddr开展的。因为前面都是在IP层处理的,而IP层是不使用端口号的。所以这里是用传入的商品来初始化当前inet实例的目的商品。
240-242 初始化sk的对应的inet_connection_sock的icsk_ext_hdr_len。这里根据成员变量的名称大概可以猜到是关于inet_connection相关的控制协议的头部长度。
244 设置tp的rx_opt成员中的mss_clamp。clamp是夹子的意思,那么这里代表接收队列的最大报文长度,是用于滑动窗口协议中的一个参数。
251 把sk的状态设置为TCP_SYN_SENT。虽然还没有对外发出SYN包,但还是提前设置了TCP的状态为TCP_SYN_SENT。这里给出的解释是,由于在此之后还要继续初始化,为了避免此sk被系统释放掉,要改下它的状态。我们可以这样理解,TCP有一个守护里程,会定期清理那些处于TCP_CLOSE状态的连接。
OK这次先写这么多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值