🌈欢迎来到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”的一些关键点:
-
效率: 与“select”和“poll”等旧机制不同,“epoll”旨在随着文件描述符的数量而有效扩展。它特别适用于具有大量开放套接字的方案。
-
事件驱动: ‘epoll’ 是事件驱动的,这意味着当文件描述符上发生特定事件时,它可以通知您的程序。事件可以包括可供读取的数据、可供写入的空间或文件描述符上的错误。
-
边沿触发和电平触发模式:** “epoll”支持边沿触发和电平触发模式。在边缘触发模式下,仅当文件描述符的状态发生更改时,才会触发事件。在关卡触发模式下,只要条件成立,就会触发事件。
-
可扩展性: ‘epoll’ 以其可扩展性而闻名。它可以有效地处理大量文件描述符,而不会出现旧机制中的性能下降。
-
系统调用: 与 ‘epoll’ 相关的主要系统调用是 ‘epoll_create’、‘epoll_ctl’ 和 ‘epoll_wait’。
“epoll”的使用通常出现在高性能网络服务器中,例如 Web 服务器或代理,在这些服务器中,处理大量并发连接对于效率至关重要。
需要注意的是,虽然 ‘epoll’ 是特定于 Linux 的,但其他操作系统也有类似的机制,但名称不同,例如 FreeBSD 和 macOS 上的 ‘kqueue’
1.1通俗例子说明:
“epoll”就像一个繁忙的办公楼的智能接待员。想象一下,你有很多房间(文件描述符或套接字),人们(数据或事件)一直在来来去去。
-
效率: 智能接待员(带有“epoll”的 Linux 内核)不会浪费时间不断检查每个房间。相反,它确切地知道哪些房间发生了一些重要的事情。
-
事件驱动: 当房间内发生重要事件(例如要读取的新数据或要写入的空间)时,智能接待员会立即通知您。你不必一直问每个房间是否有变化。
-
通知类型: 当重要的事情只发生一次(边缘触发)或只要是真的(级别触发)时,智能接待员就可以通知您。这就像告诉你什么时候有人进入房间,或者什么时候有人在那里。
-
处理多个房间: 即使您有很多房间(大量文件描述符或套接字),聪明的接待员也擅长跟踪所有房间而不会不知所措。
-
智能系统呼叫: 要与智能接待员合作,您可以采取特殊操作,例如告诉接待员开始关注新房间(“epoll_create”)、要求它注意房间的某些事件(“epoll_ctl”)和等待通知(“epoll_wait”)。
因此,简单来说,“epoll”是 Linux 中的一个智能系统,它可以帮助程序有效地管理和跟踪同时发生的许多事情,例如在不减慢速度的情况下处理大量互联网连接。这就像拥有一个智能助手,可以准确地告诉您在繁忙的办公室中何时何地发生事情。
1.2 epoll实现过程介绍
- 创建“epoll”实例:
- 首先使用“epoll_create()”或“epoll_create1()”系统调用创建一个“epoll”实例。这将返回与“epoll”实例关联的文件描述符。
- 注册文件描述符:
- 告诉“epoll”实例要监视哪些文件描述符(套接字,在网络上下文中)以及您感兴趣的事件(例如,读取、写入或错误事件)。这是使用“epoll_ctl()”系统调用完成的。
- 您可以在一次调用中注册对多个文件描述符和事件的兴趣。
- 等待事件:
- 然后,程序使用“epoll_wait()”系统调用来等待事件。
- ‘epoll_wait()’ 阻止程序,直到一个或多个注册事件发生。
- 处理事件:
- 当事件发生时,‘epoll_wait()’ 返回,您将获得有关事件的信息。
- 然后,您可以循环访问返回的事件,并根据事件类型(例如,读取或写入数据)执行必要的操作。
- 边沿触发与电平触发:
- ‘epoll’ 支持边沿触发和电平触发模式。
- 在边缘触发模式下,仅当文件描述符的状态发生变化时,您才会收到通知,而不一定在数据可用时通知。
- 在电平触发模式下,只要条件成立,您就会收到通知。
- 修改和注销文件描述符:**
- 您可以动态修改正在监控的文件描述符集,或使用“epoll_ctl()”取消注册它们。
- 关闭“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 eventsince 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 tomonitor——告诉 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 sysdef 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.stdinepoll.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()returnelif fd == sys.stdin.fileno():try:data = input() # 服务器发消息给客户端except EOFError: # 按下 ctrl+d 让服务器断开print("I want to go")new_client.close()s.close()returnnew_client.send(data.encode('utf8'))# Move these outside the loopnew_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 sysdef tcp_client():if len(sys.argv) == 1:returnclient = 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.stdinepoll.register(client.fileno(), select.EPOLLIN)epoll.register(sys.stdin.fileno(), select.EPOLLIN)while True:# 谁的缓冲区有数据 就填写进eventsevents = 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()
运行结果 :
创作不易 点赞支持~