【Python基础】网络编程之Epoll使用一(符实操:基于epoll实现的实时聊天室)

00

🌈欢迎来到Python专栏
🙋🏾‍♀️作者介绍:前PLA队员 目前是一名普通本科大三的软件工程专业学生
🌏IP坐标:湖北武汉
🍉 目前技术栈:C/C++、Linux系统编程、计算机网络、数据结构、Mysql、Python(目前在学)
🍇 博客介绍:通过分享学习过程,加深知识点的掌握,也希望通过平台能认识更多同僚,如果觉得文章有帮助,请您动动发财手点点赞,本人水平有限,有不足之处欢迎大家扶正~
🍓 最后送大家一句话共勉:知不足而奋进,望远山而前行。愿大家都能早日进大厂实现财富自由~

网络编程之Epoll使用

  • 1.什么是epoll
    • 1.1通俗例子说明:
    • 1.2 epoll实现过程介绍
  • 2.epoll相关参数介绍
  • 3.基于epoll实现的实时聊天室
    • 运行结果 :

1.什么是epoll

“epoll”是输入/输出事件通知的系统调用。它是 Linux 内核提供的一项功能,用于有效处理大量文件描述符或套接字。“epoll”在需要监视许多 I/O 操作的情况下特别有用,例如在处理大量并发连接的服务器中。

以下是关于“epoll”的一些关键点:

  1. 效率: 与“select”和“poll”等旧机制不同,“epoll”旨在随着文件描述符的数量而有效扩展。它特别适用于具有大量开放套接字的方案。

  2. 事件驱动: ‘epoll’ 是事件驱动的,这意味着当文件描述符上发生特定事件时,它可以通知您的程序。事件可以包括可供读取的数据、可供写入的空间或文件描述符上的错误。

  3. 边沿触发和电平触发模式:** “epoll”支持边沿触发和电平触发模式。在边缘触发模式下,仅当文件描述符的状态发生更改时,才会触发事件。在关卡触发模式下,只要条件成立,就会触发事件。

  4. 可扩展性: ‘epoll’ 以其可扩展性而闻名。它可以有效地处理大量文件描述符,而不会出现旧机制中的性能下降。

  5. 系统调用: 与 ‘epoll’ 相关的主要系统调用是 ‘epoll_create’、‘epoll_ctl’ 和 ‘epoll_wait’。

“epoll”的使用通常出现在高性能网络服务器中,例如 Web 服务器或代理,在这些服务器中,处理大量并发连接对于效率至关重要。

需要注意的是,虽然 ‘epoll’ 是特定于 Linux 的,但其他操作系统也有类似的机制,但名称不同,例如 FreeBSD 和 macOS 上的 ‘kqueue’

1.1通俗例子说明:

“epoll”就像一个繁忙的办公楼的智能接待员。想象一下,你有很多房间(文件描述符或套接字),人们(数据或事件)一直在来来去去。

  1. 效率: 智能接待员(带有“epoll”的 Linux 内核)不会浪费时间不断检查每个房间。相反,它确切地知道哪些房间发生了一些重要的事情。

  2. 事件驱动: 当房间内发生重要事件(例如要读取的新数据或要写入的空间)时,智能接待员会立即通知您。你不必一直问每个房间是否有变化。

  3. 通知类型: 当重要的事情只发生一次(边缘触发)或只要是真的(级别触发)时,智能接待员就可以通知您。这就像告诉你什么时候有人进入房间,或者什么时候有人在那里。

  4. 处理多个房间: 即使您有很多房间(大量文件描述符或套接字),聪明的接待员也擅长跟踪所有房间而不会不知所措。

  5. 智能系统呼叫: 要与智能接待员合作,您可以采取特殊操作,例如告诉接待员开始关注新房间(“epoll_create”)、要求它注意房间的某些事件(“epoll_ctl”)和等待通知(“epoll_wait”)。

因此,简单来说,“epoll”是 Linux 中的一个智能系统,它可以帮助程序有效地管理和跟踪同时发生的许多事情,例如在不减慢速度的情况下处理大量互联网连接。这就像拥有一个智能助手,可以准确地告诉您在繁忙的办公室中何时何地发生事情。

1.2 epoll实现过程介绍

  1. 创建“epoll”实例:
  • 首先使用“epoll_create()”或“epoll_create1()”系统调用创建一个“epoll”实例。这将返回与“epoll”实例关联的文件描述符。
  1. 注册文件描述符:
  • 告诉“epoll”实例要监视哪些文件描述符(套接字,在网络上下文中)以及您感兴趣的事件(例如,读取、写入或错误事件)。这是使用“epoll_ctl()”系统调用完成的。
  • 您可以在一次调用中注册对多个文件描述符和事件的兴趣。
  1. 等待事件:
  • 然后,程序使用“epoll_wait()”系统调用来等待事件。
  • ‘epoll_wait()’ 阻止程序,直到一个或多个注册事件发生。
  1. 处理事件:
  • 当事件发生时,‘epoll_wait()’ 返回,您将获得有关事件的信息。
  • 然后,您可以循环访问返回的事件,并根据事件类型(例如,读取或写入数据)执行必要的操作。
  1. 边沿触发与电平触发:
  • ‘epoll’ 支持边沿触发和电平触发模式。
  • 在边缘触发模式下,仅当文件描述符的状态发生变化时,您才会收到通知,而不一定在数据可用时通知。
  • 在电平触发模式下,只要条件成立,您就会收到通知。
  1. 修改和注销文件描述符:**
  • 您可以动态修改正在监控的文件描述符集,或使用“epoll_ctl()”取消注册它们。
  1. 关闭“epoll”实例:
  • 完成后,使用“close()”系统调用关闭“epoll”实例。

关键思想是“epoll”提供了一种有效的方法来管理大量文件描述符的 I/O 事件,而无需持续轮询。它允许程序是事件驱动的,仅在有活动时响应,而不是主动检查更改。这样可以提高资源使用效率,并为同时处理多个连接的应用程序提供更好的性能。

一般步骤

1.  Create an epoll object——创建 1 个 epoll 对象

2.  Tell the epoll object to monitor specific events on specific sockets— —告诉 epoll 对象,在指定的 socket 上监听指定的事件

3.  Ask the epoll object which sockets may have had the specified event

since the last query——询问 epoll 对象,从上次查询以来,哪些 socket发生了哪些指定的事件

4.  Perform some action on those sockets——在这些 socket 上执行一些

操作

5.  Tell the epoll object to modify the list of sockets and/or events to

monitor——告诉 epoll 对象,修改 socket 列表和(或)事件,并监控

6.  Repeat steps 3 through 5 until finished——重复步骤 3-5,直到完成

7.  Destroy the epoll object——销毁 epoll 对象

2.epoll相关参数介绍

import select 导入 select 模块

epoll = select.epoll() 创建一个 epoll 对象

epoll.register(文件句柄,事件类型) 注册要监控的文件句柄和事件事件类型:
select.EPOLLIN 可读事件select.EPOLLOUT 可写事件select.EPOLLERR 错误事件select.EPOLLHUP 客户端断开事件
epoll.unregister(文件句柄) 销毁文件句柄

epoll.poll(timeout) 当文件句柄发生变化,则会以列表的形式主动报告给用户进程,timeout

为超时时间,默认为-1,即一直等待直到文件句柄发生变化,如果指定为 1那么 epoll 每 1 秒汇报一次当前文件句柄的变化情况,如果无变化则返回空
epoll.fileno() 返回 epoll 的控制文件描述符(Return the epoll control file descriptor) epoll.modfiy(fineno,event) fineno 为文件描述符 event 为事件类型 作用是修改文件描述符所对应的事件epoll.fromfd(fileno)1 个指定的文件描述符创建 1 个 epoll 对象
epoll.close() 关闭 epoll 对象的控制文件描述符

3.基于epoll实现的实时聊天室

  • 服务器代码:
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.com
# @Time : 2023/11/13 14:21
# 即时聊天室
import socket
import select
import sys


def tcp_server():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    addr = ("175.178.47.72", 2001)
    print(addr)
    s.bind(addr)  # 绑定端口 此时并没有激活
    s.listen(128)  # listen时 端口才会激活
    new_client, client_addr = s.accept()  # 这里的new_client是指tcp服务器分配给客户端单独的服务
    print("[客户端add:%d]", client_addr)
    epoll = select.epoll()  # 创建一个epoll对象
    # 让epoll健=监控new_client sys.stdin
    epoll.register(new_client.fileno(), select.EPOLLIN)
    epoll.register(sys.stdin.fileno(), select.EPOLLIN)
    while True:
        # 谁的缓冲区有数据,就填写到events,events是列表里边存的是元组,(fd,事件)
        events = epoll.poll(-1)
        for fd, event in events:
            if fd == new_client.fileno():
                data = new_client.recv(100)
                if data:
                    print(f"接收到地址为{client_addr}发送的信息:{data.decode('utf8')}")
                else:
                    print(f"地址为{client_addr}的客户端已断开链接")
                    new_client.close()
                    return
            elif fd == sys.stdin.fileno():
                try:
                    data = input()  # 服务器发消息给客户端
                except EOFError:  # 按下 ctrl+d 让服务器断开
                    print("I want to go")
                    new_client.close()
                    s.close()
                    return
                new_client.send(data.encode('utf8'))

        # Move these outside the loop
    new_client.close()
    s.close()


if __name__ == '__main__':
    tcp_server()

  • 客户端代码
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.com
# @Time : 2023/11/13 14:39
import socket
import select
import sys


def tcp_client():
    if len(sys.argv) == 1:
        return
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    dest_addr = ("175.178.47.72", 2001)
    client.connect(dest_addr)
    epoll = select.epoll()  # 创建一个epoll对象
    # 让epoll监控new_client sys.stdin
    epoll.register(client.fileno(), select.EPOLLIN)
    epoll.register(sys.stdin.fileno(), select.EPOLLIN)
    while True:
        # 谁的缓冲区有数据 就填写进events
        events = epoll.poll(-1)
        for fd, event in events:
            if fd == client.fileno():
                data = client.recv(200)
                if data:
                    print("客户端[%d]接收到服务器消息:%s", dest_addr, data.decode('utf8'))
                else:
                    print("对方断开了...")
            elif fd == sys.stdin.fileno():
                data = input()  # 客户端端说话 发给对方
                client.send(data.encode('utf8'))
    client.close()


if __name__ == '__main__':
    tcp_client()

运行结果 :

01

创作不易 点赞支持~
000

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

wpf devexpress设置行和编辑器

如下教程示范如何计算行布局,特定的表格单元编辑器,和格式化显示值。这个教程基于前一个文章 选择行显示 GridControl为所有字段生成行和绑定数据源,如果AutoGenerateColumns 属性选择AddNew。添加行到GridControl精确显示为特别的几行设置。…

1688商品采集api接口1688代购商品采集API商品详情数据获取

做小程序商城时,最崩溃的瞬间是什么? 一定是当你有几百件商品,却要一件一件编辑商品名称、规格、上传图片吧…… 为了帮助商家快速上货开店,特意提供了1688的获取商品详情数据的接口,方便商家一键采集淘宝、天猫、京…

电动自动换刀高速电主轴的技术优势浅析

在制造业中,自动化技术的发展一直是一个重要的话题。其中,电动自动换刀被认为是一项高效、智能、先进的技术,在高速电主轴中使用电动自动换刀这一技术,不仅能够缩短换刀时间,还能减少换刀失误,本文将探讨Sy…

Leetcode-110 平衡二叉树

递归实现 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

“探寻服务器的无限潜能:从创意项目到在线社区,你会做什么?”

文章目录 每日一句正能量前言什么是服务器?服务器能做什么?服务器怎么用?部署创意项目,还是在线社区亦或做其他的?后记 每日一句正能量 未知的下一秒,千万不要轻言放弃。 前言 在数字化时代,服…

ctf之流量分析学习

链接:https://pan.baidu.com/s/1e3ZcfioIOmebbUs-xGRnUA?pwd9jmc 提取码:9jmc 前几道比较简单,是经常见、常考到的类型 1.pcap——zip里 流量分析里有压缩包 查字符串或者正则表达式,在包的最底层找到flag的相关内容 我们追踪…

【DP】背包问题全解

一.简介 DP(动态规划)背包问题是一个经典的组合优化问题,通常用来解决资源分配的问题,如货物装载、投资组合优化等。问题的核心思想是在有限的资源约束下,选择一组物品以最大化某种价值指标,通常是总价值或…

跨越编程界限:C++到JavaSE的平滑过渡

JDK安装 安装JDK 配置环境变量: Path 内添加 C:\Program Files\Java\jdk1.8.0_201\bin 添加 JAVA_HOME C:\Program Files\Java\jdk1.8.0_201 添加 CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar 第一个Java程序 HelloWorld.java public class…

蓝桥杯 插入排序

插入排序的思想 插入排序是一种简单直观的排序算法,其基本思想是将待排序的元素逐个插入到已排序序列 的合适位置中,使得已排序序列逐渐扩大,从而逐步构建有序序列,最终得到完全有序的序 列。 它类似于我们打扑克牌时的排序方式&…

常用的一些LDO芯片及使用稳定的LDO芯片推荐

LDO也是电赛中常用的电源模块。相比DCDC以及稳压器,LDO的跌落电压更小,因此两者适用场合不同。下面介绍一些常用的LDO及其使用: 1. TPS7A4501(正降压) 数据手册:https://www.ti.com.cn/cn/lit/ds/symlink…

高效的终极秘诀

你好,我是 EarlGrey,一名双语学习者,会一点编程,目前已翻译出版《Python 无师自通》、《Python 并行编程手册》等书籍。 点击上方蓝字关注我,持续获取优质好书、高效工具分享,一起提升认知和思维。 本文作者…

自己动手重装电脑Win10系统方法教程

如果我们自己电脑系统出现问题了,无法通过简单的操作解决,这时候最佳的解决方法,就是给电脑重装安装操作系统。有用户想给电脑重装Win10系统,但不清楚具体的重装步骤方法,下面小编就给大家详细介绍自己手动重新安装Win…

vue v-model

一、为什么使用v-model? v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。本质上是语法糖,负责监听用户的输入事件来更新数据。 二、什么场景下会使用v-model? ①…

一文带你深度体验DevChat

目录 🚀DevChat基本介绍 🕍 概述 🕍 优势 🕍 功能概述 🚀DevChat的安装 🕍 安装依赖软件 🕍 VS Code安装插件 🕍 获取和设置Access Key 🕍 版本不兼容处理【BU…

快递鸟荣获全球电子商务创业创新大赛总决赛一等奖

日前,以“开放、连接、协同、赋能”为主题,由商务部中国国际电子商务中心指导,浙江省商务厅、中共省委组织部、中共省委宣传部、中共省委网信办、省发展和改革委、省教育厅、省科技厅、省财政厅、省人力社保厅、团省委主办,湖州市…

ENVI IDL:如何编写多IF-ELSE结构?

01 前言 最近在整理代码框架结构,对于之前的一些逻辑框架进行重新梳理,我一度以为在IDL中并没有设计多IF-ELSE结构,只能单IF结构或者IF-ELSE结构,我尝试从帮助中寻找相关多IF-ELSE结构,但似乎并没有结果,暂…

Spring中Bean实例化方式和Bean生命周期

Spring Bean的实例化方式通过构造方法实例化通过简单工厂模式实例化通过工厂方法模式实例化通过FactoryBean接口实例化 注入自定义DateBean的生命周期Bean的循环依赖问题 Bean的实例化方式 Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说…

OSG查看版本信息和32or64位

使用osgversiond命令; -h,显示帮助; osg使用了OpenThreads库,也可以查看OpenThreads的版本号; -r 或 -read,读取贡献者名单文件;没看到啥; 然后进入VS开发人员命令提示;…

使用 Electron 来替代本地调试线上代理的场景

Cookie Samesite 问题 https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure?hlzh-cnhttps://www.chromium.org/updates/same-site/https://github.com/GoogleChromeLabs/samesite-exampleshttps://releases.electronjs.org/releases/s…

【LeetCode刷题-前缀和】--303.区域和检索-数组不可变

303.区域和检索-数组不可变 方法:前缀和 存储数组nums的值,每次调用sumRange时,通过循环的方法计算数组nums从下标i到下标j范围内的元素和,需要计算j-i1个元素的和,由于每次检索的时间和检索的下标范围有关&#xff0…