Chinaunix首页 | 论坛 | 博客
  • 博客访问: 69144
  • 博文数量: 11
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-06 15:04
文章分类
文章存档

2018年(11)

我的朋友

分类: LINUX

2018-04-03 15:46:59

UNIX域套接字用于在同一台机器上运行的进程之间的通信.虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高.为什么unix套接字的效率更高呢,好像是因为在实现层面,UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文.网络套接字处理了很多与网络相关的内容,还要查找路由,或者查找发送接口,总之就是相当麻烦,本地的套接字就简单多了,其实只是单纯的把一个进程想要发送的内容拷贝到内核里,再由内核把这段内存放在接收端的接收链表里,然后再拷贝到用户空间,就这么简单。

UNIX域套接字支持两种类型的套接字,stream sockets(与TCP相同) 和datagram socket(与UDP相同), 后续在详细讲解实现的时候,我们会看到数据流服务与数据报服务的大不同。

unix socket stream通信示例:
server

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <errno.h>
  7. #include <sys/un.h>

  8. void str_echo(int sockfd)
  9. {
  10.     ssize_t n;
  11.     char recv_buff[1024] = {0};
  12.     
  13.     while ((n = read(sockfd, recv_buff, sizeof(recv_buff))) > 0) {
  14.         printf("recv from client %s\n", recv_buff);
  15.         write(sockfd, recv_buff, n);
  16.         memset(recv_buff, 0, sizeof(recv_buff));
  17.     }
  18.     
  19. }
  20. int main(int argc, char **argv)
  21. {
  22.     int ret = 0;
  23.     int sockfd = 0;
  24.     int connectfd = 0;
  25.     struct sockaddr_un addr;
  26.     struct sockaddr_un client_addr;
  27.     socklen_t addrlen = sizeof(addr);

  28.     pid_t childpid = 0;
  29.     
  30.     if (argc != 2) {
  31.         printf("Usage:%s \n", argv[0]);
  32.         return -1;
  33.     }
  34.     
  35.     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  36.     if (sockfd < 0) {
  37.         printf("socket create error:%s\n", strerror(errno));
  38.         return -1;
  39.     }
  40.     
  41.     memset(&addr, 0, sizeof(addr));
  42.     addr.sun_family = AF_UNIX;
  43.     strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path));
  44.     unlink(argv[1]);
  45.         
  46.     ret = bind(sockfd, (struct sockaddr *)&addr, addrlen);
  47.     if (ret < 0) {
  48.         printf("socket bind error:%s\n", strerror(errno));
  49.         close(sockfd);
  50.         return -1;
  51.     }
  52.     
  53.     if (listen(sockfd, 10) < 0) {
  54.         printf("socket listen error:%s\n", strerror(errno));
  55.         close(sockfd);
  56.         return -1;
  57.     }
  58.     
  59.     while (1) {
  60.         connectfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
  61.         if (connectfd < 0) {
  62.             printf("accept error:%s\n", strerror(errno));
  63.             continue;
  64.         }
  65.         
  66.         childpid = fork();
  67.         if (childpid == 0) {
  68.             close(sockfd);
  69.             str_echo(connectfd);
  70.             exit(0);
  71.         }
  72.         close(connectfd);
  73.     }
  74.     
  75.     close(sockfd);
  76.     return 0;
  77. }
socket服务器端的代码大多类似,socket->bind->listen->accept,接收客户端的连接之后,从客户端接收数据,并发送回去。
client:

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <errno.h>
  4. #include <sys/un.h>
  5. #include <sys/sendfile.h>
  6. #include <fcntl.h>

  7. void show_flag(int fd)
  8. {
  9.     int val = fcntl(fd, F_GETFL);
  10.     
  11.     if (val == -1) {
  12.         printf("fcntl error for F_GETFL");
  13.         return;
  14.     }
  15.     
  16.     printf("state:%d\n", val);
  17.     
  18.     if (val & O_APPEND)
  19.         printf("append\n");
  20.         
  21.     switch (val & O_APPEND) {
  22.     case 1:
  23.         printf("open witch O_APPEND");
  24.         break;
  25.     }
  26. }

  27. void str_cli(FILE *fp, int sockfd)
  28. {
  29.     char input_buff[1024] = {0};
  30.     char recv_buff[1024] = {0};    
  31.     int ret = 0;

  32.     while (fgets(input_buff, sizeof(input_buff), fp)) {
  33.         write(sockfd, input_buff, strlen(input_buff));
  34.         
  35.         if (read(sockfd, recv_buff, sizeof(recv_buff)) > 0) {
  36.             printf("recv from server:%s\n", recv_buff);
  37.         }
  38.         memset(recv_buff, 0, sizeof(recv_buff));
  39.         memset(input_buff, 0, sizeof(input_buff));
  40.     }


  41. /*
  42.     while (1) {
  43.         show_flag(STDIN_FILENO);
  44.         
  45.         
  46.         ret = sendfile(sockfd, STDIN_FILENO, NULL, 1024);
  47.         if (ret < 0) {
  48.         //    printf("sendfile error:%s\n", strerror(errno));
  49.             continue;
  50.         }
  51.         
  52.         if (read(sockfd, recv_buff, sizeof(recv_buff)) > 0) {
  53.             printf("recv from server:%s\n", recv_buff);
  54.         }
  55.         memset(recv_buff, 0, sizeof(recv_buff));            
  56.     }
  57. */
  58. }

  59. int main(int argc, char **argv)
  60. {
  61.     int ret = 0;
  62.     int sockfd = 0;
  63.     struct sockaddr_un server_addr;
  64.     socklen_t addrlen = sizeof(server_addr);

  65.     
  66.     if (argc != 2) {
  67.         printf("Usage:%s \n", argv[0]);
  68.         return -1;
  69.     }
  70.     
  71.     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  72.     if (sockfd < 0) {
  73.         printf("socket create error:%s\n", strerror(errno));
  74.         return -1;
  75.     }
  76.     
  77.     memset(&server_addr, 0, sizeof(server_addr));
  78.     server_addr.sun_family = AF_UNIX;
  79.     strncpy(server_addr.sun_path, argv[1], sizeof(server_addr.sun_path));
  80.     
  81.     ret = connect(sockfd, (struct sockaddr *)&server_addr, addrlen);
  82.     if (ret < 0) {
  83.         printf("connect %s error:%s\n", argv[1], strerror(errno));
  84.         close(sockfd);
  85.         return -1;
  86.     }
  87.     
  88.     printf("connect to %s success\n", argv[1]);
  89.     str_cli(stdin, sockfd);
  90.     
  91.     close(sockfd);
  92.     return 0;
  93. }
客户端把服务器绑定的地址写入connect的第二个参数中,连接服务器,接收输入,然后发送到服务器,服务器将接受到的数据再发送给客户端。

创建socket,执行socket(AF_UNIX, SOCK_STREAM, 0),三个参数分别是协议族,socket类型,协议类型(最后一个我没有用过,基本都是填写0),在这个不对第三个参数进行分析,我们的重点放在前面两个参数
协议族有很多 包括但不限于
AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)
       AF_IPX              IPX - Novell protocols
       AF_NETLINK          Kernel user interface device     netlink(7)
       AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
       AF_AX25             Amateur radio AX.25 protocol
       AF_ATMPVC           Access to raw ATM PVCs
       AF_APPLETALK        AppleTalk                        ddp(7)
       AF_PACKET           Low level packet interface       packet(7)
       AF_ALG              Interface to kernel crypto API

有这么多,其实说白了,bsd socket是一系列的通用通信接口,支持不同种类的socket,我们一般用的是AF_INET(IPV4通信),AF_UNIX(本地通信),AF_NETLINK(应用层与内核层通信),在我的工作中这三类socket都会用到,后续一点一点的写吧

socket函数在内核中执行的是SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
  2. {
  3.     int retval;
  4.     struct socket *sock;
  5.     int flags;

  6.     /* Check the SOCK_* constants for consistency. */
  7.     BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
  8.     BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
  9.     BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
  10.     BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

  11.     flags = type & ~SOCK_TYPE_MASK;
  12.     if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
  13.         return -EINVAL;
  14.     type &= SOCK_TYPE_MASK;

  15.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
  16.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

  17.     retval = sock_create(family, type, protocol, &sock);
  18.     if (retval < 0)
  19.         goto out;

  20.     retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
  21.     if (retval < 0)
  22.         goto out_release;

  23. out:
  24.     /* It may be already another descriptor 8) Not kernel problem. */
  25.     return retval;

  26. out_release:
  27.     sock_release(sock);
  28.     return retval;
  29. }
内核中的函数都挺复杂的,这个函数做了很多检查,检查参数,在这里不再一一描述了,因为很多选项我们平时不会用到,用到了再写也不迟,今天的重点是socket创建,socket系统调用调用创建socket 的函数sock_create(family, type, protocol, &sock);struct socket该结构体socket 主要使用在BSD socket 层,是最上层的结构,也就是说这个结构体离我们的应用层最近,这是我们进入内核之后接触到的第一层
sock_create实际调用_sock_create函数进行socket创建,

点击(此处)折叠或打开

  1. int __sock_create(struct net *net, int family, int type, int protocol,
  2.              struct socket **res, int kern)
  3. {
  4.     int err;
  5.     struct socket *sock;
  6.     const struct net_proto_family *pf;

  7.     /*
  8.      * Check protocol is in range
  9.      */
  10.     if (family < 0 || family >= NPROTO)
  11.         return -EAFNOSUPPORT;
  12.     if (type < 0 || type >= SOCK_MAX)
  13.         return -EINVAL;

  14.     /* Compatibility.

  15.      This uglymoron is moved from INET layer to here to avoid
  16.      deadlock in module load.
  17.      */
  18.     if (family == PF_INET && type == SOCK_PACKET) {
  19.         pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
  20.              current->comm);
  21.         family = PF_PACKET;
  22.     }

  23.     err = security_socket_create(family, type, protocol, kern);
  24.     if (err)
  25.         return err;

  26.     /*
  27.      *    Allocate the socket and allow the family to set things up. if
  28.      *    the protocol is 0, the family is instructed to select an appropriate
  29.      *    default.
  30.      */
  31.     sock = sock_alloc();
  32.     if (!sock) {
  33.         net_warn_ratelimited("socket: no more sockets\n");
  34.         return -ENFILE;    /* Not exactly a match, but its the
  35.                  closest posix thing */
  36.     }

  37.     sock->type = type;

  38. #ifdef CONFIG_MODULES
  39.     /* Attempt to load a protocol module if the find failed.
  40.      *
  41.      * 12/09/1996 Marcin: this makes REALLY only sense, if the user
  42.      * requested real, full-featured networking support upon configuration.
  43.      * Otherwise module support will
  44.      */
  45.     if (rcu_access_pointer(net_families[family]) == NULL)
  46.         request_module("net-pf-%d", family);
  47. #endif

  48.     rcu_read_lock();
  49.     pf = rcu_dereference(net_families[family]);
  50.     err = -EAFNOSUPPORT;
  51.     if (!pf)
  52.         goto out_release;

  53.     /*
  54.      * We will call the ->create function, that possibly is in a loadable
  55.      * module, so we have to bump that loadable module refcnt first.
  56.      */
  57.     if (!try_module_get(pf->owner))
  58.         goto out_release;

  59.     /* Now protected by module ref count */
  60.     rcu_read_unlock();

  61.     err = pf->create(net, sock, protocol, kern);
  62.     if (err < 0)
  63.         goto out_module_put;

  64.     /*
  65.      * Now to bump the refcnt of the [loadable] module that owns this
  66.      * socket at sock_release time we decrement its refcnt.
  67.      */
  68.     if (!try_module_get(sock->ops->owner))
  69.         goto out_module_busy;

  70.     /*
  71.      * Now that we're done with the ->create function, the [loadable]
  72.      * module can have its refcnt decremented
  73.      */
  74.     module_put(pf->owner);
  75.     err = security_socket_post_create(sock, family, type, protocol, kern);
  76.     if (err)
  77.         goto out_sock_release;
  78.     *res = sock;

  79.     return 0;

  80. out_module_busy:
  81.     err = -EAFNOSUPPORT;
  82. out_module_put:
  83.     sock->ops = NULL;
  84.     module_put(pf->owner);
  85. out_sock_release:
  86.     sock_release(sock);
  87.     return err;

  88. out_release:
  89.     rcu_read_unlock();
  90.     goto out_sock_release;
  91. }

pf = rcu_dereference(net_families[family]);根据协议族类型找到协议创建socket的函数,我们这里是AF_UNIX,使用的函数是unix_create,不同的协议有不同的创建函数,这里只是长征的第一步

点击(此处)折叠或打开

  1. static int unix_create(struct net *net, struct socket *sock, int protocol,
  2.          int kern)
  3. {
  4.     if (protocol && protocol != PF_UNIX)
  5.         return -EPROTONOSUPPORT;

  6.     sock->state = SS_UNCONNECTED;

  7.     switch (sock->type) {
  8.     case SOCK_STREAM:
  9.         sock->ops = &unix_stream_ops;
  10.         break;
  11.         /*
  12.          *    Believe it or not BSD has AF_UNIX, SOCK_RAW though
  13.          *    nothing uses it.
  14.          */
  15.     case SOCK_RAW:
  16.         sock->type = SOCK_DGRAM;
  17.     case SOCK_DGRAM:
  18.         sock->ops = &unix_dgram_ops;
  19.         break;
  20.     case SOCK_SEQPACKET:
  21.         sock->ops = &unix_seqpacket_ops;
  22.         break;
  23.     default:
  24.         return -ESOCKTNOSUPPORT;
  25.     }

  26.     return unix_create1(net, sock, kern) ? 0 : -ENOMEM;
  27. }
根据socket类型初始化sock->ops,然后调用unix_create1进行struct sock传输控制块的初始化

点击(此处)折叠或打开

  1. static struct sock *unix_create1(struct net *net, struct socket *sock, int kern)
  2. {
  3.     struct sock *sk = NULL;
  4.     struct unix_sock *u;

  5.     atomic_long_inc(&unix_nr_socks);
  6.     if (atomic_long_read(&unix_nr_socks) > 2 * get_max_files())
  7.         goto out;

  8.     sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_proto, kern);
  9.     if (!sk)
  10.         goto out;

  11.     sock_init_data(sock, sk);
  12.     lockdep_set_class(&sk->sk_receive_queue.lock,
  13.                 &af_unix_sk_receive_queue_lock_key);

  14.     sk->sk_allocation    = GFP_KERNEL_ACCOUNT;
  15.     sk->sk_write_space    = unix_write_space;
  16.     sk->sk_max_ack_backlog    = net->unx.sysctl_max_dgram_qlen;
  17.     sk->sk_destruct        = unix_sock_destructor;
  18.     u     = unix_sk(sk);
  19.     u->path.dentry = NULL;
  20.     u->path.mnt = NULL;
  21.     spin_lock_init(&u->lock);
  22.     atomic_long_set(&u->inflight, 0);
  23.     INIT_LIST_HEAD(&u->link);
  24.     mutex_init(&u->iolock); /* single task reading lock */
  25.     mutex_init(&u->bindlock); /* single task binding lock */
  26.     init_waitqueue_head(&u->peer_wait);
  27.     init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);
  28.     unix_insert_socket(unix_sockets_unbound(sk), sk);
  29. out:
  30.     if (sk == NULL)
  31.         atomic_long_dec(&unix_nr_socks);
  32.     else {
  33.         local_bh_disable();
  34.         sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
  35.         local_bh_enable();
  36.     }
  37.     return sk;
  38. }

unix_create1返回值是struct sock*结构,这是我们接触到的第二层,传输控制块结构,这是一个很大的结构,做的事情很多,后续介绍。
最后返回时,会执行sock_map_fd,这一步是将socket与文件绑定,分配文件描述符。至此socket就创建成功了





阅读(1905) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~