【网络编程】poll

news2024/12/25 23:57:49

主旨思想

  • 用一个结构体记录文件描述符集合,并记录用户态状态和内核态状态

函数说明

  • 概览
#include <poll.h> 
struct pollfd { 
    int fd; /* 委托内核检测的文件描述符 */ 
    short events; /* 委托内核检测文件描述符的什么事件 */ 
    short revents; /* 文件描述符实际发生的事件 */ 
};

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • int poll(struct pollfd *fds, nfds_t nfds, int timeout); 

          通过man poll查看帮助
          参数
               fds:是一个struct pollfd 结构体数组,这是一个需要检测的文件描述符的集合
               nfds:这个是第一个参数数组中最后一个有效元素的下标 + 1
               timeout:阻塞时长
                         0:不阻塞
                        -1:阻塞,当检测到需要检测的文件描述符有变化,解除阻塞
                       >0:具体的阻塞时长(ms)
         返回值
              -1:失败
              >0(n):检测的集合中有n个文件描述符发生了变化

events及revents取值,如果有多个事件需要检测,用|即可,如同时检测读和写:POLLIN|POLLOUT

代码实现

注意事项

  • nfds表示的监听文件描述符的下标,所以在遍历时,需要使用fds[i].fd取得相应的文件描述符
  • 如何优雅的更新nfds?代码中使用连接的文件描述符作为替代更新

服务器端:
 

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>

#define SERVERIP "127.0.0.1"
#define PORT 6789


int main()
{
    // 1. 创建socket(用于监听的套接字)
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1) {
        perror("socket");
        exit(-1);
    }
    // 2. 绑定
    struct sockaddr_in server_addr;
    server_addr.sin_family = PF_INET;
    // 点分十进制转换为网络字节序
    inet_pton(AF_INET, SERVERIP, &server_addr.sin_addr.s_addr);
    // 服务端也可以绑定0.0.0.0即任意地址
    // server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);
    int ret = bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (ret == -1) {
        perror("bind");
        exit(-1);
    }
    // 3. 监听
    ret = listen(listenfd, 8);
        if (ret == -1) {
        perror("listen");
        exit(-1);
    }
    
    struct pollfd fds[1024];
    // 初始化
    for (int i = 0; i < 1024; i++) {
        fds[i].fd = -1;
        fds[i].events = POLLIN;
    }
    // 将监听文件描述符加入
    fds[0].fd = listenfd;
    int nfds = 0;
    // 不断循环等待客户端连接
    while (1) {
        // 使用poll,设置为永久阻塞,有文件描述符变化才返回
        int num = poll(fds, nfds + 1, -1);
        if (num == -1) {
            perror("poll");
            exit(-1);
        } else if (num == 0) {
            // 当前无文件描述符有变化,执行下一次遍历
            // 在本次设置中无效(因为select被设置为永久阻塞)
            continue;
        } else {
            // 首先判断监听文件描述符是否发生改变(即是否有客户端连接)
            if (fds[0].revents & POLLIN) {
                // 4. 接收客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                int connfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (connfd == -1) {
                    perror("accept");
                    exit(-1);
                }
                // 输出客户端信息,IP组成至少16个字符(包含结束符)
                char client_ip[16] = {0};
                inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip));
                unsigned short client_port = ntohs(client_addr.sin_port);
                printf("ip:%s, port:%d\n", client_ip, client_port);
                // 遍历集合, 将新的需要监听的文件描述符加入集合
                for (int i = 1; i < 1024; i++) {
                    if (fds[i].fd == -1) {
                        fds[i].fd = connfd;
                        fds[i].events = POLLIN;
                        break;
                    }
                }
                // 更新最大的监听文件描述符集合下标
                // 存在问题:使用文件描述符替代最大对应下标
                nfds = nfds > connfd ? nfds : connfd;
            }

            // 遍历集合判断是否有变动,如果有变动,那么通信
            char recv_buf[1024] = {0};
            for (int i = 1; i <= nfds; i++) {
                if (fds[i].fd != -1 && fds[i].revents & POLLIN) {
                    ret = read(fds[i].fd, recv_buf, sizeof(recv_buf));
                    if (ret == -1) {
                        perror("read");
                        exit(-1);
                    } else if (ret > 0) {
                        printf("recv server data : %s\n", recv_buf);
                        write(fds[i].fd, recv_buf, strlen(recv_buf));
                    } else {
                        // 表示客户端断开连接
                        printf("client closed...\n");
                        close(fds[i].fd);
                        fds[i].fd = -1;
                        break;
                    }
                }
            }
        }
    }

    close(listenfd);
    return 0;
}

客户端:

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define SERVERIP "127.0.0.1"
#define PORT 6789

int main()
{
    // 1. 创建socket(用于通信的套接字)
    int connfd = socket(AF_INET, SOCK_STREAM, 0);
    if (connfd == -1) {
        perror("socket");
        exit(-1);
    }
    // 2. 连接服务器端
    struct sockaddr_in server_addr;
    server_addr.sin_family = PF_INET;
    inet_pton(AF_INET, SERVERIP, &server_addr.sin_addr.s_addr);
    server_addr.sin_port = htons(PORT);
    int ret = connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (ret == -1) {
        perror("connect");
        exit(-1);
    }
    // 3. 通信
    char recv_buf[1024] = {0};
    while (1) {
        // 发送数据
        char *send_buf = "client message";
        write(connfd, send_buf, strlen(send_buf));
        // 接收数据
        ret = read(connfd, recv_buf, sizeof(recv_buf));
        if (ret == -1) {
            perror("read");
            exit(-1);
        } else if (ret > 0) {
            printf("recv server data : %s\n", recv_buf);
        } else {
            // 表示客户端断开连接
            printf("client closed...\n");
        }
        // 休眠的目的是为了更好的观察,放在此处可以解决read: Connection reset by peer问题
        sleep(1);
    }
    // 关闭连接
    close(connfd);
    return 0;
}

存在问题(缺点)

  • 缺点同select第一点和第二点(如下),即解决了第三点和第四点
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/828229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL 远程操作mysql

可以让别人在他们的电脑上操作我电脑上的数据库 create user admin identified with mysql_native_password by admin; //设置账号密码都为admingrant all on *.* to admin; //给admin账号授权 授权完成

​五、驱动 - ​音频系统硬件电路

文章目录 1. 音频系统硬件电路结构2. 蓝牙音频2.1 音乐播放2.2 VoIP通话2.3 4G通话3. 其他3.1 什么是S/PDIF1. 音频系统硬件电路结构 录音放音设备:mic、speaker、耳机、听筒这些带有录音放音功能的设备(因为录放设备可能是模拟设备也可能是数字设备,所以接口可能是模拟接口…

IDEA中怎么使用git下载项目到本地,通过URL克隆项目(gitee\github)

点击 新建>来自版本控制的项目 点击后会弹出这样一个窗口 通过URL拉取项目代码 打开你要下载的项目仓库 克隆>复制 gitee github也是一样的 返回IDEA 将刚刚复制的URL粘贴进去选择合适的位置点击克隆 下载完成

JavaEE初阶之网络初识

一、网络发展史 1.1独立模式 独立模式:计算机之间相互独立; 1.2网络互连 随着时代的发展,越来越需要计算机之间互相通信,共享软件和数据,即以多个计算机协同工作来完成业务,就有了网络互连。网络互连:将多台计算机连接在一起,完成数据共享。 数据共享本质是网络数据…

【Python】5分钟了解11个最佳的Python编译器和解释器

11个最佳Python编译器和解释器 1. Brython2. Pyjs3. WinPython4. Skulpt5. Shed Skin6. Active Python7. Transcrypt8. Nutika9. Jython10. CPython11. IronPython结论原文链接 Python是一门初学者的编程语言。它是一种高级语言&#xff0c;非常灵活、解释性和面向对象的语言。…

安防监控进入全景时代,萤石全景摄像机E4p体验评测

随着智能家居的普及&#xff0c;智能家居摄像机已经成为我们必备的智能家居设备之一。传统摄像机在捕捉画面时只能获得单一角度的画面&#xff0c;可能会错过关键信息。 针对这个问题&#xff0c;萤石最近推出的E4p全景摄像机&#xff0c;解决了用户在特定场景下需要更全面画面…

笔试数据结构选填题

目录 卡特兰数Catalan&#xff1a;出栈序列/二叉树数 树 二叉树 N01N2 哈夫曼树&#xff08;最优二叉树&#xff09;Huffman 度m的哈夫曼树只有度为0和m的结点&#xff1a;Nm(n-1)/(m-1) 平衡二叉树AVL Nh表示深度为h最少结点数&#xff0c;则N00&#xff0c;N11&#…

Linux下TCP网络服务器与客户端通信程序入门

文章目录 目标服务器与客户端通信流程TCP服务器代码TCP客户端代码 目标 实现客户端连接服务器&#xff0c;通过终端窗口发送信息给服务器端&#xff0c;服务器接收到信息后对信息数据进行回传&#xff0c;客户端读取回传信息并返回。 服务器与客户端通信流程 TCP服务器代码 …

AI 绘画Stable Diffusion 研究(五)sd文生图功能详解(下)

大家好&#xff0c;我是风雨无阻。 上一篇文章详细介绍了sd文生图的功能及使用注意事项&#xff0c;感兴趣的朋友可以前往查看&#xff1a;AI 绘画Stable Diffusion 研究&#xff08;四&#xff09;sd文生图功能详解&#xff08;上&#xff09; 。 那今天这篇文章&#xff0c;我…

【牛客网】二叉搜索树与双向链表

二叉搜索树与双向链表 题目描述算法分析编程代码 链接: 二叉搜索树与双向链表 题目描述 算法分析 编程代码 /* struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(int x) :val(x), left(NULL), right(NULL) {} };*/ class Solution { public:…

解决Hadoop审计日志hdfs-audit.log过大的问题

【背景】 新搭建的Hadoop环境没怎么用&#xff0c;就一个环境天天空跑&#xff0c;结果今天运维告诉我说有一台服务器磁盘超过80%了&#xff0c;真是太奇怪了&#xff0c;平台上就跑了几个spark测试程序&#xff0c;哪来的数据呢&#xff1f; 【问题调查】 既然是磁盘写满了&…

第七章 图论

第七章 图论 一、数据结构定义 图的邻接矩阵存储法#define MaxVertexNum 100 // 节点数目的最大值// 无边权&#xff0c;只用0或1表示边是否存在 bool graph[MaxVertexNum][MaxVertexNum];// 有边权 int graph[MaxVertexNum][MaxVertexNum];图的邻接表存储法 把所有节点存储为…

python手机编程软件app下载,用手机编程python的软件

本篇文章给大家谈谈python手机编程软件app下载&#xff0c;以及python编程手机软件哪个好&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 前言 相信多数安卓用户都使用过Qpython这款移动端的Python编辑器吧&#xff1f;之前我也研究过一阵子这个工具&#xf…

轮足机器人硬件总结

简介 本文主要根据“轮腿机器人Hyun”总结的硬件部分。 轮腿机器人Hyun开源地址&#xff1a;https://github.com/HuGuoXuang/Hyun 1 电源部分 1.1 78M05 78M05是一款三端稳压器芯片&#xff0c;它可以将输入电压稳定输出为5V直流电压. 1.2 AMS1117-3.3 AMS1117-3.3是一种输…

钉钉群消息推送

1. 添加钉钉群机器人 PC端登录&#xff08;当前版本手机端无法进行推送关键词设置&#xff09;&#xff0c;群设置--> 机器人 --> webhook进行安全设置复制webhook对应的url 2. 群消息推送 钉钉群消息支持纯文本和markdown类型 2.1 调用示例源码 import com.alibaba.…

【Python】导函数 及 求解微分方程

如何用Python求解微分方程&#xff0c;主要是基于Python的 sympy 库来进行微分运算&#xff0c;sympy库的 diff函数主要用于导函数&#xff0c;dsolve函数用于解微分方程。 导函数 import sympy as sp# 定义符号变量 x sp.symbols(x)# 定义函数 f sp.sin(x) * sp.exp(x)# 求…

SpringBoot整合Elasticsearch(最新最全,高效安装到使用)

文章目录 一、安装Elasticsearch相关插件1.选择版本2.安装Elasticsearch3.安装node4.安装grunt5.安装es-head插件6.安装kibana7.安装ik分词器 二、整合SpringBoot和Elasticearch1.pom.xml2.application.yml3.ElasticSearch&#xff08;实体类&#xff09;4.ElasticSearchReposi…

devops(前端)

1.前言 前端的打包流程和后端的流程是一样的&#xff0c;只是打包的环境和制作的镜像有所不同&#xff0c;前端需要使用nodejs环境打包&#xff0c;镜像也是使用nginx镜像&#xff0c;因为用的是k8s的pod运行镜像&#xff0c;还需要使用configmap挂载nginx的配置&#xff0c;一…

04|Oracle学习(外键约束)

备注&#xff1a;下文中出现的 add constraints应改为add constraint&#xff0c;不需要加s 1.外键约束介绍 1.1 什么是外键约束&#xff1a; 如果按下面的设计&#xff0c;直接在原表中添加“班级编号”、“班级名称”、“班主任”、“班级描述”这些列名&#xff0c;会出现…

# Windows 环境下载 Android 12源码

前言 Android 官网&#xff08;该方式不适合 Windows 平台&#xff09;&#xff1a;https://source.android.com/source/downloading.html (备注自 2021 年 6 月 22 日起&#xff0c;安卓操作系统不再支持在 Windows 或 MacOS 上进行构建&#xff0c;如果要编译源码推荐先安装…