我的编程空间,编程开发者的网络收藏夹
学习永远不晚

【计算机网络】网络编程接口 Socket API 解读(3)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

【计算机网络】网络编程接口 Socket API 解读(3)

         Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。

        本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。


poll

poll()           遵循 POSIX.1 - 2008

ppoll()         遵循 Linux

1.库

标准 c 库,libc, -lc

2.头文件

3.接口定义

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);       int ppoll(struct pollfd *fds, nfds_t nfds,                 const struct timespec *_Nullable tmo_p,                 const sigset_t *_Nullable sigmask);

4.接口描述

        poll() 和 select() 做的事情差不多,它等待一个文件描述符集 I/O 就绪。Linux 的 epoll() 也是类似的,只是比 poll() 提供多了一些特性。

        fds 参数是要监控的文件描述符集,是下面结构体的一个数组:

           struct pollfd {               int   fd;                        short events;                    short revents;               };

        由调用者指定 fds 的项数。

        结构体中 fd 包含了一个打开的文件描述符,如果它是负值,那么 events 参数将被忽略,revents 返回 0。(也就是说 可以将 fd 设置为其补码就可以忽略它)。

        events 参数是一个输入参数,通过按位掩码来标识应用感兴趣的文件描述符上的事件。参数可以设置为 0,那么就只能返回 POLLHUP/POLLERR/POLLNVAL 事件。

        revents 是一个输出参数,由内核填充实际发生的事件。这些事件可以是 events 中指定的事件,也可以是  POLLHUP/POLLERR/POLLNVAL 中的一个。(events 中这三个事件对应的位并没有什么意义,只要对应的条件发生,revents 就会返回该事件。)

        如果没有请求的事件(包括错误)发生,那么 poll() 会一直阻塞,直到有事件发生。

        timeout 参数指定了 poll() 等待文件描述符就绪的毫秒数,该调用会一直阻塞直到:

  • 文件描述符就绪
  • 调用被信号打断
  • 发生超时

        同样,timeout 值也会向上近似到系统时钟粒度,由于内核调度延迟阻塞的事件可能会稍微多一点。如果 timeout 是负值,表示超时时间是无限长。如果 timeout 设置为 0,那么 poll() 会马上返回,即使没有任何文件描述符就绪。

        events 和 revents 中各个位在 poll.h 中定义:

    POLLIN

       有数据可以读。

    POLLPRI

        文件描述符上有异常发生,可能是(1)TCP socket 上有带外数据(2)处于报文模式的伪终端主机发现了从机状态变化(3)cgroup.events 文件被修改了。

        POLLOUT

       当前可写,但是写大于 socket 或 pipe 中可用空间的数据仍然会导致阻塞(除非设置了 O_NONBLOCK)。

        POLLRDHUP

        流 socket 对端关闭了连接或者在写半连接时关机。这个定义依赖于 _GNU_SOURCE 宏定。

        POLLERR

       发生错误。如果文件描述符指向了 pipe 的写端,而读端关闭了,那么也会返回这个错误。

        POLLHUP

        挂断。在读取 pipe 或者流 socket 时,这个事件只表示对端关闭了其通道,后面的数据读取时,在通道中数据读尽后再继续读会返回 0(EOF)。

        POLLNVAL

        请求不合法:fd 没有打开。

        在使用 _XOPEN_SOURCE 宏编译时,还会有以下一些事件,不过也没有提供太多信息:

        POLLRDNORM

        等同于 POLLIN。

        POLLRDBAND

        优先带宽数据可以读(通常在 Linux 上用)

        POLLWRNORM

        等同于 POLLOUT

        POLLWRBAND

        可能写了优先数据

ppoll()

        ppoll() 和 poll() 的关系就像 select() 和 pselect() 的关系一样,ppoll() 为应用提供了等待信号或者就绪事件的安全方法。

        除了 timeout 时间精度上的差异,以下两段代码几乎等效

           ready = ppoll(&fds, nfds, tmo_p, &sigmask);
           sigset_t origmask;           int timeout;           timeout = (tmo_p == NULL) ? -1 :                     (tmo_p->tv_sec * 1000 + tmo_p->tv_nsec / 1000000);           pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);           ready = poll(&fds, nfds, timeout);           pthread_sigmask(SIG_SETMASK, &origmask, NULL);

          上面代码说成几乎等效而不是等效主要是因为负值的 timeout 会被 poll() 解释为一直等待,而 ppoll() 中负值的 *tmo_p 会报错。

        可以参考 pselect(2) 来看为什么 ppoll 是必要的。

        如果 sigmask 参数为 NULL,那么就不会有任何信号屏蔽操作,这时这两个接口唯一的区别就是时间精度。

        tmo_p 指定了 ppoll() 会阻塞的时间上限,它是指向 timespec 结构体的指针,指针为空时,ppoll() 会一直阻塞。

5.返回值

        成功时,poll() 返回一个非负数表示 pollfds 中有多少个文件描述符上有事件发生,即对应的 revents 有被更新为非 0 值。返回 0 表示没有任何文件描述符就绪并超时。

        发生错误时,返回 -1,并设置errno 来指示错误类型。

        错误值定义如下:

EFAULTfds 指向了进程外的地址空间
EINTR请求事件发生前,发生了信号,具体参见 signal(7)
EIVALnfds 值超出了 RLIMIT_NOFILE 限制
EINVALppoll() 中的 *tmo_P 是一个非法值(负数)
ENOMEM没有足够内存来分配内核数据结构

一些其他 UNIX 系统上,如果内核无法发分配内核资源,poll() 可能会产生 EAGAIN 类的错误,而不像 Linux 上的 ENOMEM。POSIX 允许这种行为。所以,一个可移植的程序需要检测该错误,并重试,就像处理 EINTR 一样。

一些实现定义了非标准常量 INFTIM(-1),用作 poll() 的 timeout,但是这个常量并没有被被 glibc 提供。

6.注意

       poll() 和 ppoll() 的行为不受 O_NONBLOCK 标志影响。

        对于一个文件描述符正在被 poll() 监听却被另一个线程关闭了这种情况的讨论,可以参考 select(2)。

7.BUGS

        可以参考 select(2) 中关于虚假就绪通知的讨论。 

8.代码实例

        该程序会打开命令行参数传进来的文件名并监听其 POLLIN 事件,程序会循环调用 poll() 来监听文件描述符,打印已经就绪的文件描述符数。对于每个就绪的文件描述符,程序会:

  • 以可读的格式显示返回的 revents
  • 如果文件描述符就绪,那么就从中读一些数据出来并打印
  • 如果文件描述符不可读,但是发生了一些其他事件(比如 POLLHUP),就关闭文件描述符

        假定我们在一个终端运行程序,让他打开一个 FIFO:

       $ mkfifo myfifo       $ ./poll_input myfifo

        在另一个终端打开 FIFO,并写入一些数据,然后关闭 FIFO:

       $ echo aaaaabbbbbccccc > myfifo

                 我们将在运行程序的终端上看到如下信息:

           Opened "myfifo" on fd 3           About to poll()           Ready: 1             fd=3; events: POLLIN POLLHUP               read 10 bytes: aaaaabbbbb           About to poll()           Ready: 1             fd=3; events: POLLIN POLLHUP               read 6 bytes: ccccc           About to poll()           Ready: 1             fd=3; events: POLLHUP               closing fd 3           All file descriptors closed; bye

         从上面我们可以看到 poll() 返回了三次:

  • 第一次返回是 POLLIN,表示文件描述符可读,另一个是 POLLHUP 表示文件描述符的另一个端关闭了。程序接着读取了一些可用的输入数据
  • 第二次返回同样是这两个事件,依然消费了一些可用数据
  • 最后一次返回,poll() 只有 POLLHUP 事件,然后关闭文件描述符并结束了程序。
              #include        #include        #include        #include        #include        #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \   } while (0)       int       main(int argc, char *argv[])       {           int            ready;           char           buf[10];           nfds_t         num_open_fds, nfds;           ssize_t        s;           struct pollfd  *pfds;           if (argc < 2) {              fprintf(stderr, "Usage: %s file...\n", argv[0]);              exit(EXIT_FAILURE);           }           num_open_fds = nfds = argc - 1;           pfds = calloc(nfds, sizeof(struct pollfd));           if (pfds == NULL)               errExit("malloc");                      for (nfds_t j = 0; j < nfds; j++) {               pfds[j].fd = open(argv[j + 1], O_RDONLY);               if (pfds[j].fd == -1)                   errExit("open");               printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);               pfds[j].events = POLLIN;           }                      while (num_open_fds > 0) {               printf("About to poll()\n");               ready = poll(pfds, nfds, -1);               if (ready == -1)                   errExit("poll");               printf("Ready: %d\n", ready);                              for (nfds_t j = 0; j < nfds; j++) {                   if (pfds[j].revents != 0) {                       printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,  (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",  (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",  (pfds[j].revents & POLLERR) ? "POLLERR " : "");                       if (pfds[j].revents & POLLIN) {                           s = read(pfds[j].fd, buf, sizeof(buf));                           if (s == -1)   errExit("read");                           printf("    read %zd bytes: %.*s\n",      s, (int) s, buf);                       } else {                                           printf("    closing fd %d\n", pfds[j].fd);                           if (close(pfds[j].fd) == -1)   errExit("close");                           num_open_fds--;                       }                   }               }           }           printf("All file descriptors closed; bye\n");           exit(EXIT_SUCCESS);       }

下一篇 【计算机网络】网络编程接口 Socket API 解读(4)​​​​​​​

来源地址:https://blog.csdn.net/BillyThe/article/details/132774658

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

【计算机网络】网络编程接口 Socket API 解读(3)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

计算机网络编程 | 多路I/O转接服务器

欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学
2023-08-18

计算机网络中外部链接消失的解决方法

这篇文章主要介绍了计算机网络中外部链接消失的解决方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。如果你从事SEO行业有一段时间了,你会发现很多SEO专家都习惯统计自己的外链
2023-06-13

win7系统计算机网络连接错误711如何解决

很多使用win7系统的小伙伴一定都遇到过网络连接错误711的问题,win7系统计算机网络连接错误711如何解决?今日小编就带给大家一个关于win7电脑网络连接错误711的解决方法。win7系统计算机网络连接错误711如何解决:1.打开计算机
2023-07-17

计算机网络编程 | 并发服务器代码实现(多进程/多线程)

欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学
2023-08-18

计算机网络中编写c++程序一般需经过哪些步骤

这篇文章给大家分享的是有关计算机网络中编写c++程序一般需经过哪些步骤的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。编写c++程序一般需经过的几个步骤依次是编辑、调试、编译、连接。c++程序是c语言的继承,它不仅
2023-06-14

计算机网络中怎么样将高级语言编写的程序翻译成机器语言程序

小编给大家分享一下计算机网络中怎么样将高级语言编写的程序翻译成机器语言程序,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!将高级语言编写的程序翻译成机器语言程序,可采用两种翻译方式:编译和解释。解释方式是将源程序逐句解释执行
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录