歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核--網絡協議棧深入分析(五)--套接字的綁定、監聽、連接和斷開

Linux內核--網絡協議棧深入分析(五)--套接字的綁定、監聽、連接和斷開

日期:2017/3/1 10:09:26   编辑: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. sock = sockfd_lookup_light(fd, &err, &fput_needed);
  7. if (sock) {
  8. err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);
  9. if (err >= 0) {
  10. err = security_socket_bind(sock,
  11. (struct sockaddr *)&address,
  12. addrlen);
  13. if (!err)
  14. err = sock->ops->bind(sock,
  15. (struct sockaddr *)
  16. &address, addrlen);
  17. }
  18. fput_light(sock->file, fput_needed);
  19. }
  20. return err;
  21. }

首先調用函數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. *err = -EBADF;
  6. file = fget_light(fd, fput_needed);
  7. if (file) {
  8. sock = sock_from_file(file, err);
  9. if (sock)
  10. return sock;
  11. fput_light(file, *fput_needed);
  12. }
  13. return NULL;
  14. }

上面函數中先調用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. //RAW類型套接字若有自己的bind函數,則使用之
  10. if (sk->sk_prot->bind) {
  11. err = sk->sk_prot->bind(sk, uaddr, addr_len);
  12. goto out;
  13. }
  14. err = -EINVAL;
  15. .....................
  16. //地址合法性檢查
  17. chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
  18. /* Not specified by any standard per-se, however it breaks too
  19. * many applications when removed. It is unfortunate since
  20. * allowing applications to make a non-local bind solves
  21. * several problems with systems using dynamic addressing.
  22. * (ie. your servers still start up even if your ISDN link
  23. * is temporarily down)
  24. */
  25. err = -EADDRNOTAVAIL;
  26. if (!sysctl_ip_nonlocal_bind &&
  27. !(inet->freebind || inet->transparent) &&
  28. addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
  29. chk_addr_ret != RTN_LOCAL &&
  30. chk_addr_ret != RTN_MULTICAST &&
  31. chk_addr_ret != RTN_BROADCAST)
  32. goto out;
  33. snum = ntohs(addr->sin_port);
  34. err = -EACCES;
  35. if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
  36. goto out;
  37. /* We keep a pair of addresses. rcv_saddr is the one
  38. * used by hash lookups, and saddr is used for transmit.
  39. *
  40. * In the BSD API these are the same except where it
  41. * would be illegal to use them (multicast/broadcast) in
  42. * which case the sending device address is used.
  43. */
  44. lock_sock(sk);
  45. /* Check these errors (active socket, double bind). */
  46. err = -EINVAL;
  47. if (sk->sk_state != TCP_CLOSE || inet->inet_num)//如果sk的狀態是CLOSE或者本地端口已經被綁定
  48. goto out_release_sock;
  49. inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;//設置源地址
  50. if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
  51. inet->inet_saddr = 0; /* Use device */
  52. /* Make sure we are allowed to bind here. */
  53. if (sk->sk_prot->get_port(sk, snum)) {
  54. inet->inet_saddr = inet->inet_rcv_saddr = 0;
  55. err = -EADDRINUSE;
  56. goto out_release_sock;
  57. }
  58. if (inet->inet_rcv_saddr)
  59. sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
  60. if (snum)
  61. sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
  62. inet->inet_sport = htons(inet->inet_num);//設置源端口號,標明該端口已經被占用
  63. inet->inet_daddr = 0;
  64. inet->inet_dport = 0;
  65. sk_dst_reset(sk);
  66. err = 0;
  67. out_release_sock:
  68. release_sock(sk);
  69. out:
  70. return err;
  71. }

這樣套接字綁定結束。

Copyright © Linux教程網 All Rights Reserved