在说connect调用之前,先简单看下inet_aton,这个函数完成的是ip地址的转换,它是将一个IP地址转换为一个4字节的整数。ok,回到我们的connect函数,首先,connect是一个系统调用。如下:
kernel\net\Socket.c
SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,int, addrlen)
{struct socket *sock;struct sockaddr_storage address;int err, fput_needed;//之前在socket的创建的时候,内核生成了一个struct file *file结构,而且这个结构被关联到了fd描述符,这里要做的就是通过这个fd找到file指针,然后通过file->private_data找到sock指针,因为在socket创建的时候文件的私有数据里面存放的就是这个socket的指针sock = sockfd_lookup_light(fd, &err, &fput_needed);if (!sock)goto out;//这个就是将用户态的IP地址信息拷贝到内核态中,之后地址就存放到了address结构中err = move_addr_to_kernel(uservaddr, addrlen, &address);if (err < 0)goto out_put;//安全相关的检查,忽略err =security_socket_connect(sock, (struct sockaddr *)&address, addrlen);if (err)goto out_put;//在上一篇文章的socket创建过程中,我们讲到过sock->ops对于TCP而言被赋值为inet_stream_opserr = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,sock->file->f_flags);//最后通过nlmsg_multicast将socket连接的消息发送出去 if (!err)sockev_notify(SOCKEV_CONNECT, sock);
out_put:fput_light(sock->file, fput_needed);
out:return err;
}
继续分析inet_stream_ops函数
kernel\net\ipv4\Af_inet.c
const struct proto_ops inet_stream_ops = {.family     = PF_INET,....connect    = inet_stream_connect,...
}
inet_stream_connect
 __inet_stream_connect(sock, uaddr, addr_len, flags);
  switch (sock->state) {
   ...
   之前我们在socket创建的时候是将当前socket的状态设置为未连接的状态,现在进行钻港台的改变
   caseSS_UNCONNECTED:
   err = sk->sk_prot->connect(sk, uaddr, addr_len);
   sock->state = SS_CONNECTING;
   break;
  }
  sock->state =SS_CONNECTED;
针对我们目前的TCP协议而言,这里的sk->sk_prot指向是tcp_prot,所以其connect函数就是tcp_v4_connect
struct proto tcp_prot = {.name          = "TCP",.owner           = THIS_MODULE,.close           = tcp_close,.connect       = tcp_v4_connect,...
}

对于 TCP 协议来说,其连接实际上就是发送一个 SYN 报文,在服务器的应答到来时,回答它一
个 ack 报文,也就是完成三次握手中的第一和第三次。 
 
要发送 SYN 报文,也就是说,需要有完整的来源/目的地址,来源/目的端口,目的地址/端口由用户
态提交,但是问题是没有自己的地址和端口,因为并没有调用过 bind(2),一台主机,对于端口,
可以像 sys_bind()那样,从本地未用端口中动态分配一个,那地址呢?因为一台主机可能会存在多
个 IP地址,如果随机动态选择,那么有可能选择一个错误的来源地址,将不能正确地到达目的地
址。换句话说,来源地址的选择,是与路由相关的。 
 
调用路由查找的核心函数 ip_route_output_flow(),在没有提供来源地址的情况下,会根据实际情况,
调用 inet_select_addr()函数来选择一个合适的。同时,如果路由查找命中,会生成一个相应的路由
缓存项,这个缓存项,不但对当前发送SYN报文有意义,对于后续的所有数据包,都可以起到一
个加速路由查找的作用。这一任务,是通过 ip_route_connect()函数完成的,它返回相应的路由缓存
项(也就是说,来源地址也在其中了):

int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;//这里的inet_sock和tcp_sock需要特别的说明下:这个结构是在socket创建的时候分配的,其大小为.obj_size  = sizeof(struct tcp_sock),如下:
/*
struct tcp_sockstruct inet_connection_sockstruct inet_sock struct sock  sk;
*/
    //因此我们可以知道我们这儿的inet和tp指针是向下转型struct inet_sock *inet = inet_sk(sk);struct tcp_sock *tp = tcp_sk(sk);__be16 orig_sport, orig_dport;//目标地址和ip层的下一跳地址__be32 daddr, nexthop;struct flowi4 *fl4;struct rtable *rt;int err;struct ip_options_rcu *inet_opt;...//先将下一跳地址和目标地址设置用户设置的目的地址nexthop = daddr = usin->sin_addr.s_addr;inet_opt = rcu_dereference_protected(inet->inet_opt,sock_owned_by_user(sk));/* 如果使用源地址路由,则需要改变下一跳的地址,我们应用的APP并没有设置源路由,因此上一步返回的inet_opt指针实际是空指针*/if (inet_opt && inet_opt->opt.srr) {if (!daddr)return -EINVAL;nexthop = inet_opt->opt.faddr;}/* 本端端口,目前可能已绑定,也可能没分配,针对我们的APP而言这个值没有设置为0*/orig_sport = inet->inet_sport;//用户设置的目的IP的端口orig_dport = usin->sin_port;fl4 = &inet->cork.fl.u.ip4;inet->inet_saddr这个参数同样为0,也就是说当前源地址和源端口都是为0的rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,IPPROTO_TCP,orig_sport, orig_dport, sk, true);if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {ip_rt_put(rt);return -ENETUNREACH;}if (!inet_opt || !inet_opt->opt.srr)daddr = fl4->daddr;if (!inet->inet_saddr)inet->inet_saddr = fl4->saddr;inet->inet_rcv_saddr = inet->inet_saddr;if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {/* Reset inherited state */tp->rx_opt.ts_recent     = 0;tp->rx_opt.ts_recent_stamp = 0;if (likely(!tp->repair))tp->write_seq     = 0;}if (tcp_death_row.sysctl_tw_recycle &&!tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr)tcp_fetch_timewait_stamp(sk, &rt->dst);inet->inet_dport = usin->sin_port;inet->inet_daddr = daddr;inet_csk(sk)->icsk_ext_hdr_len = 0;if (inet_opt)inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;/* Socket identity is still unknown (sport may be zero).* However we set state to SYN-SENT and not releasing socket* lock select source port, enter ourselves into the hash tables and* complete initialization after this.*/tcp_set_state(sk, TCP_SYN_SENT);err = inet_hash_connect(&tcp_death_row, sk);if (err)goto failure;rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,inet->inet_sport, inet->inet_dport, sk);if (IS_ERR(rt)) {err = PTR_ERR(rt);rt = NULL;goto failure;}/* OK, now commit destination to socket.  */sk->sk_gso_type = SKB_GSO_TCPV4;sk_setup_caps(sk, &rt->dst);if (!tp->write_seq && likely(!tp->repair))tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,inet->inet_daddr,inet->inet_sport,usin->sin_port);inet->inet_id = tp->write_seq ^ jiffies;err = tcp_connect(sk);rt = NULL;if (err)goto failure;return 0;}

Linux网络编程之connect创建相关推荐

  1. Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解

    Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解 (1)sockaddr struct sockaddr { unsigned  short  sa_ ...

  2. Linux网络编程之IP地址转换为无符号整数的方法

    Linux网络编程之IP地址转换为无符号整数的方法,代码如下:(没考虑异常输入) #include <stdio.h> #include <string.h> #include ...

  3. Linux网络编程之TCP状态转移

    Linux网络编程之TCP状态转移 一.TCP状态转移时序 二.半关闭及shutdown函数 一.TCP状态转移时序 TCP状态转移图: netstat -apn | grep client 查看客户 ...

  4. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  5. linux ioctl网络参数设置,Linux 网络编程之ioctl函数

    1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...

  6. 嵌入式linux ntpd命令,嵌入式Linux网络编程之:实验内容——NTP协议实现

    本文引用地址:http://www.eepw.com.cn/article/257114.htm 10.4实验内容--NTP协议实现 1.实验目的 通过实现NTP协议的练习,进一步掌握Linux网络编 ...

  7. linux网络编程之Socket编程

    (1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...

  8. Linux网络编程之sockaddr与sockaddr_in,sockaddr_un分析

    sockaddr struct sockaddr { unsigned short sa_family; /* address family, AF_xxx */ char sa_data[14]; ...

  9. Linux网络编程之Socket套接字

    一.Socket到底是什么 socket 这个英文单词的原意是"插口""插槽", 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发.你 ...

  10. Linux网络编程之socket文件传输示例

    本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. ...

最新文章

  1. 摆地摊创业赚钱完全详细攻略
  2. Python 网络爬虫 001 (科普) 网络爬虫简介
  3. 学密码学一定得学程序
  4. hdu4004 The Frog's Games 二分
  5. Shell脚本中$的用法
  6. android view滑动到顶部并固定在顶部
  7. Lightroom 如何安装lrplugin格式插件
  8. JavaScript学习(八十一)—将多维数组转化为一维数组
  9. Android RelativeLayout 属性
  10. 手机 html5 hammer drag widget,javascript – HTML5使用Hammer.js拖放事件拖放div上的元素
  11. 生产调度系统算法模型简要设计
  12. sip协议的超时机制
  13. win10重置计算机网络设置,为你解答win10下如何重置网络
  14. 计算机休眠唤醒后没声音,MacBook Pro从睡眠模式中唤醒后突然没有声音
  15. Difference between UDP and TCP
  16. 2023广东海洋大学计算机考研信息汇总
  17. 利率里面的BP是什么意思,bp是什么意思贷款利率
  18. 服务器和PC Server介绍
  19. 一对一直播源码,实现一个简单的登录界面
  20. c++ 0x8000ffff灾难性故障_硬盘出了故障就换?教你一招,不花一分钱就能修复!...

热门文章

  1. ASP.NET身份验证机制membership入门——API篇
  2. spring catch了异常还是回滚了_干货:Spring 踩坑之@Transactional 神奇失效
  3. defaultView与currentStyle的区别_获取CSS样式值
  4. 关于js执行机制的理解
  5. C语言编程入门——程序练习(下)
  6. jenkins发送构建邮件配置项中文
  7. [转载] 中华典故故事(孙刚)——33 人上一百形形色色
  8. GridView.RowCommand 事件
  9. 无盘工作站与VMware View虚拟桌面对比
  10. RHEL5 配置yum