Chinaunix首页 | 论坛 | 博客
  • 博客访问: 812450
  • 博文数量: 321
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 936
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-23 11:25
文章分类

全部博文(321)

文章存档

2017年(1)

2016年(10)

2015年(61)

2014年(187)

2013年(62)

分类: LINUX

2015-03-06 10:26:54

原文地址:tcp_v4_connect函数分析 作者:abc78400123

当创建了TCP协议套接字后,客户端再调用connect()函数请求建立TCP链接。该函数通过系统调用触发tcp_v4_connect函数的执行,产生一个包含SYN标志和一个32位的序号的连接请求包,并发送给服务器端。
这是TCP三次握手的第一步。
我们知道,两个不同主机间的进程要进行通信,他们所发送的数据包所经过的路径为:
应用层→传输层→网络层→网络介质层
所以tcp_v4_connect()函数发出的SYN标志的请求包也要经过这些路径。

传输层负责的是不同应用程序之间的通信。即不同端口
网络层负责的使不同主机之间的通信,即不同的IP地址。在这期间数据包的传输通过路由来实现。路由发生在网络层。
时间戳:发送方在报文中放置一个时间戳值,接受方在确认时返回这个值以及接受时的时间戳值,以此来计算RTT值。
TCP超时与重传中最重要的部分就是对一个给定连接的往返时间( RTT)的测量。由于路由器和网络流量均会变化,因此我们认为这个时间可能经常会发生变化, TCP应该跟踪这些变化并相应地改变其超时时间。

MSS:表示TCP传往另一端的最大块数据长度,在连接建立时(SYN)双方各自通告自己的Mss,否则默认为536字节(加上20字节IP报头和TCP报头构成576字节的IP数据报)。长度为4字节

在一般的体制中,I P可以从T C P、U D P、I C M P和I G M P接收数据报(即在本地生成的数据
报)并进行发送,或者从一个网络接口接收数据报(待转发的数据报)并进行发送。IP层在内存中有一个路由表。当收到一份数据报并进行发送时,它都要对该表搜索一次。当数据报来自某个网络接口时, IP首先检查目的IP地址是否为本机的I P地址之一或者I P广播地址。如果确实是这样,数据报就被送到由I P首部协议字段所指定的协议模块进行处理。如果数据报的目的不是这些地址,那么( 1)如果I P层被设置为路由器的功能,那么就对数据报进行转发(也就是说,像下面对待发出的数据报一样处理);否则( 2)数据报被丢弃。

由上述分析这个函数应包含路由,端口,产生SYN分节,生成序号,发送包等部分。

754 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)

{
756         struct inet_opt *inet = inet_sk(sk);
//变量inet指向套接字struct sock中的inet选项
757         struct tcp_opt *tp = tcp_sk(sk);
//变量tp指向套接字struct sock中的TCP选项
758         struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
//将通用地址结构转换为IPv4的地址结构
759         struct rtable *rt;
//记录路由表项
760         u32 daddr, nexthop;
//daddr记录目的地址,nesthop记录下一条地址
761         int tmp;
762         int err;
763 
//判断地址长度是否合法,应该要大于或者等于其地址的长度
764         if (addr_len < sizeof(struct sockaddr_in))
765                 return -EINVAL;
//判断是否为inet协议族 
767         if (usin->sin_family != AF_INET)
768                 return -EAFNOSUPPORT;
//初始化目的地址和下一条地址
770         nexthop = daddr = usin->sin_addr.s_addr;
771         if (inet->opt && inet->opt->srr) {
772                 if (!daddr)
773                         return -EINVAL;
774                 nexthop = inet->opt->faddr;
775         }
//查找路由表项,并通过变量rt记录下来
777         tmp = ip_route_connect(&rt, nexthop, inet->saddr,
778                                RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
779                                IPPROTO_TCP,
780                                inet->sport, usin->sin_port, sk);
781         if (tmp < 0)
782                 return tmp;
783 
784         if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
785                 ip_rt_put(rt);
786                 return -ENETUNREACH;
787         }
788 
789         if (!inet->opt || !inet->opt->srr)
790                 daddr = rt->rt_dst;//如果源地址为0,则把rt_ src赋给源地址
791 
792         if (!inet->saddr)
793                 inet->saddr = rt->rt_src;
794         inet->rcv_saddr = inet->saddr;
//初始化TCP选项
796         if (tp->ts_recent_stamp && inet->daddr != daddr) {
797                 /* Reset inherited state */
798                 tp->ts_recent       = 0;//Time stamp to echo next
799                 tp->ts_recent_stamp = 0;//Time we stored ts_recent (for aging) 
800                 tp->write_seq       = 0;//序号初始化为0
801         }
802 
803         if (sysctl_tcp_tw_recycle &&
804             !tp->ts_recent_stamp && rt->rt_dst == daddr) {
805                 struct inet_peer *peer = rt_get_peer(rt);
806 
807                 /* VJ's idea. We save last timestamp seen from
808                  * the destination in peer table, when entering state TIME-WAIT
809                  * and initialize ts_recent from it, when trying new connection.
810                  */
811 
812                 if (peer && peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) {
813                         tp->ts_recent_stamp = peer->tcp_ts_stamp;
814                         tp->ts_recent = peer->tcp_ts;
815                 }
816         }
817 
818         inet->dport = usin->sin_port;//目的端口地址
819         inet->daddr = daddr;//目的IP地址
820 
821         tp->ext_header_len = 0;
822         if (inet->opt)
823                 tp->ext_header_len = inet->opt->optlen;//inet的opt选项的长度
824 
825         tp->mss_clamp = 536;
826 
827         /* Socket identity is still unknown (sport may be zero).
828          * However we set state to SYN-SENT and not releasing socket
829          * lock select source port, enter ourselves into the hash tables and
830          * complete initialization after this.
831          */
//把套接字结构sk的状态置为TCP_SYN_SENT
832         tcp_set_state(sk, TCP_SYN_SENT);
//为套接字绑定一个端口,并记录在TCP的哈希表中
833         err = tcp_v4_hash_connect(sk);
834         if (err)
835                 goto failure;
836 
837         err = ip_route_newports(&rt, inet->sport, inet->dport, sk);
838         if (err)
839                 goto failure;
840 
841         /* OK, now commit destination to socket.  */
//设置套接字的路由出口信息
842         __sk_dst_set(sk, &rt->u.dst);
843         tcp_v4_setup_caps(sk, &rt->u.dst);
844         tp->ext2_header_len = rt->u.dst.header_len;
//生成一个序号
846         if (!tp->write_seq)
847                 tp->write_seq = secure_tcp_sequence_number(inet->saddr,
848                                                            inet->daddr,
849                                                            inet->sport,
850                                                            usin->sin_port);
851 
852         inet->id = tp->write_seq ^ jiffies;
//调用tcp_connect(sk)函数,为请求包设置SYN标志,并发出请求包
854         err = tcp_connect(sk);
855         rt = NULL;//rt指针指向内存0开始处
856         if (err)
857                 goto failure;
858 
859         return 0;
860 
861 failure:
//如果连接失败,则需把套接字状态设置为:TCP_CLOSE
862         /* This unhashes the socket and releases the local port, if necessary. */
863         tcp_set_state(sk, TCP_CLOSE);
864         ip_rt_put(rt);
865         sk->sk_route_caps = 0;//sk_route_caps,网络驱动特征标志
866         inet->dport = 0;
867         return err;
868
阅读(970) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~