专栏首页CoffeeLandlinux内核之socket
原创

linux内核之socket

linux socket的基础

linux的空间分为kernel space 和 user space, 比例是1:3

linux中一切皆文件, 所以用文件描述符来表示socket, 实际上是socket函数的返回值

EXAMPLE
       An example of the use of socket() is shown in (3).
accept(2), bind(2), connect(2), fcntl(2), getpeername(2), getsockname(2), getsockopt(2), ioctl(2), listen(2), read(2), recv(2), select(2),  send(2),  shutdown(2),  socketpair(2),
       write(2), getprotoent(3), ip(7), socket(7), tcp(7), udp(7), unix(7)


/* Create a new socket of type TYPE in domain DOMAIN, using
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
   Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;

建立socket 是什么

socket是提供tcp/ip操作的一个api, 相当于封装tcp和ip, 用来进程与进程之间的通信

socket如何创建

一个进程要创建socket的流程如下所示

1. 调用socket函数创建socket, 返回一个文件描述符sfd
2. 调用bind函数绑定sfd
3. listen(sfd,...)
4. 当有client要建立socket连接, 接收来的连接 并返回一个客户端文件描述符cfd = accept(sfd,...)
5. 调用read()函数, 等待read client的输入

linux socket-io模型
 #include <sys/socket.h>
       #include <sys/un.h>
       #include <stdlib.h>
       #include <stdio.h>
       #include <string.h>

       #define MY_SOCK_PATH "/somepath"
       #define LISTEN_BACKLOG 50

       #define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

       int
       main(int argc, char *argv[])
       {
		   /*
		    *  sfd - server fd
			   cfd - client fd
		    */
           int sfd, cfd;
           struct sockaddr_un my_addr, peer_addr;
           socklen_t peer_addr_size;

           sfd = socket(AF_UNIX, SOCK_STREAM, 0);
           if (sfd == -1)
               handle_error("socket");

           memset(&my_addr, 0, sizeof(struct sockaddr_un));
                               /* Clear structure */
           my_addr.sun_family = AF_UNIX;
           strncpy(my_addr.sun_path, MY_SOCK_PATH,
                   sizeof(my_addr.sun_path) - 1);

           if (bind(sfd, (struct sockaddr *) &my_addr,
                   sizeof(struct sockaddr_un)) == -1)
               handle_error("bind");

           if (listen(sfd, LISTEN_BACKLOG) == -1)
               handle_error("listen");

           /* Now we can accept incoming connections one
              at a time using accept(2) */

           peer_addr_size = sizeof(struct sockaddr_un);
           cfd = accept(sfd, (struct sockaddr *) &peer_addr,
                        &peer_addr_size);
           if (cfd == -1)
               handle_error("accept");

           /* Code to deal with incoming connection(s)... */

           /* When no longer required, the socket pathname, MY_SOCK_PATH
              should be deleted using unlink(2) or remove(3) */
       }

如何在linux上查看socket

 ll  /proc/<pid>/fd
 
 linux默认的三个文件描述符
 0 - stdin
 1 - stdout
 2 - stderr
 
 >& 是重定向到文件描述符
 

基于socket的应用

java nio

java nio用channel代替了输入输出流, 这个channel, 就是socket

其原理是使用了linux kernel里的fcntl

/* Do the file control operation described by CMD on FD.
   The remaining arguments are interpreted depending on CMD.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int fcntl (int __fd, int __cmd, ...);

nio

多路复用select

复用的意思时不用每个进程/线程来操控单独的一个IO,只需一个进程/线程来操控多个IO

但是缺点是

1. 每循环一次都要传递很多fd, 调用一次select

SELECT(2)                                                                       
Linux Programmer's Manual                                                                       
SELECT(2)

NAME
       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing
       
       
#include <stdio.h>
       #include <stdlib.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int
       main(void)
       {
           fd_set rfds;
           struct timeval tv;
           int retval;

           /* Watch stdin (fd 0) to see when it has input. */
           FD_ZERO(&rfds);
           FD_SET(0, &rfds);

           /* Wait up to five seconds. */
           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);
           /* Don't rely on the value of tv now! */

           if (retval == -1)
               perror("select()");
           else if (retval)
               printf("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true. */
           else
               printf("No data within five seconds.\n");

           exit(EXIT_SUCCESS);
       }

epoll模型

epoll - I/O event notification facility


           #define MAX_EVENTS 10
           struct epoll_event ev, events[MAX_EVENTS];
           int listen_sock, conn_sock, nfds, epollfd;

           /* Set up listening socket, 'listen_sock' (socket(),
              bind(), listen()) */

           epollfd = epoll_create(10);
           if (epollfd == -1) {
               perror("epoll_create");
               exit(EXIT_FAILURE);
           }

           ev.events = EPOLLIN;
           ev.data.fd = listen_sock;
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
               perror("epoll_ctl: listen_sock");
               exit(EXIT_FAILURE);
           }

          for (;;) {
               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
               if (nfds == -1) {
                   perror("epoll_pwait");
                   exit(EXIT_FAILURE);
               }

               for (n = 0; n < nfds; ++n) {
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                       (struct sockaddr *) &local, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       }
                       setnonblocking(conn_sock);
                       ev.events = EPOLLIN | EPOLLET;
                       ev.data.fd = conn_sock;
                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                   &ev) == -1) {
                           perror("epoll_ctl: conn_sock");
                           exit(EXIT_FAILURE);
                       }
                   } else {
                       do_use_fd(events[n].data.fd);
                   }
               }
           }

       When  used  as  an  edge-triggered  interface,  for  performance  reasons, it is possible to add the file descriptor inside the epoll interface (EPOLL_CTL_ADD) once by specifying
       (EPOLLIN|EPOLLOUT).  This allows you to avoid continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl(2) with EPOLL_CTL_MOD.
               
                

References

https://www.bilibili.com/video/BV1V7411F7NB?from=search&seid=7973277497050423363

未完待续.......

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 位运算之异或(XOR)

    一个二进制数减1, 相当于把这个数的从最右侧的1开始flio翻转

    CoffeeLand
  • Java测试框架推荐

    java有很多测试类框架, 开发中有很多比如Mokito, powermock, wiremock, cucumber ,但是powermock测试,sonar...

    CoffeeLand
  • java系统问题之cpu占用过高

    系统负载(System Load)是系统CPU繁忙程度的度量,即有多少进程在等待被CPU调度(进程等待队列的长度)。

    CoffeeLand
  • Socket编程回顾,一个最简单服务器程序

    Aichen
  • c语言基础

    编写一个循环结构的程序,求数列前20项之和: 2/1,3/2,4/3,5/4,......,20/19,21/20

    _simple
  • 1060 爱丁顿数 (25 分)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    韩旭051
  • 挑战程序竞赛系列(8):2.1一往直前!贪心法(其他)

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • C语言——小学题目B卷解析(终)

    第6题,简单说明:系统有默认的转化规则,就是从精度底的转化为精度高的,避免计算时精度的丢失。coding一下:

    Ed_Frey
  • 虚幻引擎5技术解析:几何图像的思想

    Lumen in the Land of Nanite,在PlayStation 5上运行的实时演示.

    新智元
  • 多益网络2016春季实习校招笔试回顾(C++游戏后台开发)

    2016.04.16晚中山大学大学城校区(东校区)参加了多益网络的C++游戏后台开发的笔试。有几道笔试题还是值得斟酌和记录的,特记录如下。比较可惜,因为回老家了...

    Dabelv

扫码关注云+社区

领取腾讯云代金券