Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Linux内核--网络协议栈深入分析(五)--套接字的绑定、监听、连接和断开

本文分析基于Linux Kernel 3.2.1更多请查看 Linux内核--网络内核实现分析1、套接字的绑定创建完套接字服务器端会在应用层使用bind函数惊醒套接字的绑定,这时会产生系统调用,sys_bind内核函数进行套接字。系统调用函数的具体实现
  1. SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)  
  2. {  
  3.     struct socket *sock;  
  4.     struct sockaddr_storage address;  
  5.     int err, fput_needed;  
  6.   
  7.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  8.     if (sock) {  
  9.         err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);  
  10.         if (err >= 0) {  
  11.             err = security_socket_bind(sock,  
  12.                            (struct sockaddr *)&address,  
  13.                            addrlen);  
  14.             if (!err)  
  15.                 err = sock->ops->bind(sock,  
  16.                               (struct sockaddr *)  
  17.                               &address, addrlen);  
  18.         }  
  19.         fput_light(sock->file, fput_needed);  
  20.     }  
  21.     return err;  
  22. }  
首先调用函数sockfd_lookup_light()函数通过文件描述符来查找对应的套接字sock。
  1. static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)  
  2. {  
  3.     struct file *file;  
  4.     struct socket *sock;  
  5.   
  6.     *err = -EBADF;  
  7.     file = fget_light(fd, fput_needed);  
  8.     if (file) {  
  9.         sock = sock_from_file(file, err);  
  10.         if (sock)  
  11.             return sock;  
  12.         fput_light(file, *fput_needed);  
  13.     }  
  14.     return NULL;  
  15. }  
上面函数中先调用fget_light函数通过文件描述符返回对应的文件结构,然后调用函数sock_from_file函数返回该文件对应的套接字结构体地址,它存储在file->private_data属性中。再回到sys_bind函数,在返回了对应的套接字结构之后,调用move_addr_to_kernel将用户地址空间的socket拷贝到内核空间。然后调用INET协议族的操作集中bind函数inet_bind函数将socket地址(内核空间)和socket绑定。
  1. int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)  
  2. {  
  3.     struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;  
  4.     struct sock *sk = sock->sk;  
  5.     struct inet_sock *inet = inet_sk(sk);  
  6.     unsigned short snum;  
  7.     int chk_addr_ret;  
  8.     int err;  
  9.   
  10.     //RAW类型套接字若有自己的bind函数,则使用之   
  11.     if (sk->sk_prot->bind) {  
  12.         err = sk->sk_prot->bind(sk, uaddr, addr_len);  
  13.         goto out;  
  14.     }  
  15.     err = -EINVAL;  
  16.     .....................  
  17.         //地址合法性检查   
  18.     chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);  
  19.   
  20.     /* Not specified by any standard per-se, however it breaks too 
  21.      * many applications when removed.  It is unfortunate since 
  22.      * allowing applications to make a non-local bind solves 
  23.      * several problems with systems using dynamic addressing. 
  24.      * (ie. your servers still start up even if your ISDN link 
  25.      *  is temporarily down) 
  26.      */  
  27.     err = -EADDRNOTAVAIL;  
  28.     if (!sysctl_ip_nonlocal_bind &&  
  29.         !(inet->freebind || inet->transparent) &&  
  30.         addr->sin_addr.s_addr != htonl(INADDR_ANY) &&  
  31.         chk_addr_ret != RTN_LOCAL &&  
  32.         chk_addr_ret != RTN_MULTICAST &&  
  33.         chk_addr_ret != RTN_BROADCAST)  
  34.         goto out;  
  35.   
  36.     snum = ntohs(addr->sin_port);  
  37.     err = -EACCES;  
  38.     if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))  
  39.         goto out;  
  40.   
  41.     /*      We keep a pair of addresses. rcv_saddr is the one 
  42.      *      used by hash lookups, and saddr is used for transmit. 
  43.      * 
  44.      *      In the BSD API these are the same except where it 
  45.      *      would be illegal to use them (multicast/broadcast) in 
  46.      *      which case the sending device address is used. 
  47.      */  
  48.     lock_sock(sk);  
  49.   
  50.     /* Check these errors (active socket, double bind). */  
  51.     err = -EINVAL;  
  52.     if (sk->sk_state != TCP_CLOSE || inet->inet_num)//如果sk的状态是CLOSE或者本地端口已经被绑定   
  53.         goto out_release_sock;  
  54.   
  55.     inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;//设置源地址   
  56.     if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)  
  57.         inet->inet_saddr = 0;  /* Use device */  
  58.   
  59.     /* Make sure we are allowed to bind here. */  
  60.     if (sk->sk_prot->get_port(sk, snum)) {  
  61.         inet->inet_saddr = inet->inet_rcv_saddr = 0;  
  62.         err = -EADDRINUSE;  
  63.         goto out_release_sock;  
  64.     }  
  65.   
  66.     if (inet->inet_rcv_saddr)  
  67.         sk->sk_userlocks |= SOCK_BINDADDR_LOCK;  
  68.     if (snum)  
  69.         sk->sk_userlocks |= SOCK_BINDPORT_LOCK;  
  70.     inet->inet_sport = htons(inet->inet_num);//设置源端口号,标明该端口已经被占用   
  71.     inet->inet_daddr = 0;  
  72.     inet->inet_dport = 0;  
  73.     sk_dst_reset(sk);  
  74.     err = 0;  
  75. out_release_sock:  
  76.     release_sock(sk);  
  77. out:  
  78.     return err;  
  79. }  
这样套接字绑定结束。