歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> 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. }  

這樣套接字綁定結束。

Copyright © Linux教程網 All Rights Reserved