http://www.cnblogs.com/my_life/articles/6085516.html
http://www.cnblogs.com/my_life/articles/6085588.html
http://www.cnblogs.com/my_life/articles/6065752.html
https://www.cnblogs.com/my_life/articles/6085848.html
./include/net/inet_sock.h :inet_sock
./net/ipv4/af_inet.c :inet_bind
108 struct inet_sock {
114 /* Socket demultiplex comparisons on incoming packets. */
115 __u32 daddr; //Foreign IPv4 addr,socket将要连接的外部地址
116 __u32 rcv_saddr; //Bound local IPv4 addr, rcv_saddr is the one used by hash lookups。对于组播而言,只接收该地址发来的组播数据;对于单播而言,只从该地址所代表的网卡接收数据
117 __u16 dport; //Destination port
118 __u16 num; //Local port,为用户提供的端口,不可用,出错;可用,sport = num;
119 __u32 saddr; //Sending source, saddr is used for transmit,通过该地址找到对应的发送网卡来发送数据
*** __u16 sport; //Source port
- 对于bind()操作而言,是为了绑定本地地址和端口,所以其
inet->daddr = 0;
inet->dport = 0; //无需连接外部地址
inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; //查找地址和发送地址为用户的设置
/* 若地址类型为广播或多播,则将地址置 0,表示直接使用网络设备 */
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) //是根据地址解析出来的
inet->saddr = 0; /* Use device */
//例如对于组播而言,其saddr由setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr))来设置源地址、发送地址、由哪个网卡发送。不调用该选项的话,使用系统默认的网卡发送组播数据。
//setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))来设置接收地址,由哪个网卡接收
snum = ntohs(addr->sin_port); //用户空间指定的端口
get_port(sk, snum) //确认是否可绑定端口. 若可以, 则绑定在 inet->num 之上;不可用出错
inet->sport = htons(inet->num);
若绑定了端口,则发送和接收都使用该端口
==========================================================================
IP_MULTICAST_IF
作用似乎跟bind()有些重复。对于单播的时候,有inet_sock的成员rcv_saddr==saddr(本地发送数据用到的地址=本地接收数据
的地址),当用于组播时,saddr等于0(即本地发送数据地址为0),此时不知道用哪个地址发送数据,这个任务就留给了IP_MULTICAST_IF选项。 要有接收组播数据工能时,bind()不是邦写本地主机的IP,而是邦定组播组的IP。也就是说要接收到组播组发送的数据,还得把本地的IP信息提供上去,此时就用到IP_MULTICAST_IF。
==========================================================================
对于 TCP 协议来说,其连接实际上就是发送一个 SYN 报文,在服务器的应答到来时,回答它一
个 ack 报文,也就是完成三次握手中的第一和第三次。
要发送 SYN 报文,也就是说,需要有完整的来源/目的地址,来源/目的端口,目的地址/端口由用户
态提交,但是问题是没有自己的地址和端口,因为并没有调用过 bind(2),一台主机,对于端口,
可以像 sys_bind()那样,从本地未用端口中动态分配一个,那地址呢?因为一台主机可能会存在多
个 IP地址,如果随机动态选择,那么有可能选择一个错误的来源地址,将不能正确地到达目的地
址。换句话说,来源地址的选择,是与路由相关的。
调用路由查找的核心函数 ip_route_output_slow(),在没有提供来源地址的情况下,会根据实际情况,
调用 inet_select_addr()函数来选择一个合适的。同时,如果路由查找命中,会生成一个相应的路由
缓存项,这个缓存项,不但对当前发送SYN报文有意义,对于后续的所有数据包,都可以起到一
个加速路由查找的作用。这一任务,是通过 ip_route_connect()函数完成的,它返回相应的路由缓存
项(也就是说,来源地址也在其中了)。
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
nexthop = daddr = usin->sin_addr.s_addr; // 将下一跳地址和目的地址的临时变量都暂时设为用户提交的地址。
tmp = ip_route_connect(&rt, nexthop, inet->saddr,
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
IPPROTO_TCP,
inet->sport, usin->sin_port, sk); //如果之前进行了bind(),则sport为bind()提供的端口,否则使用动态选择的端口
if (!inet->opt || !inet->opt->srr)
daddr = rt->rt_dst; // 更新目的地址临时变量——使用路由查找后返回的值。
if (!inet->saddr)
inet->saddr = rt->rt_src;
inet->rcv_saddr = inet->saddr; // 如果还没有设置源地址,和本地发送地址,则使用路由中返回的值。
inet->dport = usin->sin_port; //dport为用户提供的目的端口
inet->daddr = daddr; // 保存目的地址及端口
}