Python网络编程篇之select和epoll
1. select 原理
在多路复⽤的模型中, ⽐较常⽤的有select模型和epoll模型。 这两个都是系统接⼝, 由操作系统提供。 当然, Python的select模块进⾏了更⾼级的封装。
⽹络通信被Unix系统抽象为⽂件的读写, 通常是⼀个设备, 由设备驱动程序提供, 驱动可以知道⾃身的数据是否可⽤。 ⽀持阻塞操作的设备驱动通常会实现⼀组⾃身的等待队列, 如读/写等待队列⽤于⽀持上层(⽤户层)所需的block或non-block操作。 设备的⽂件的资源如果可⽤( 可读或者可写) 则会通知进程, 反之则会让进程睡眠, 等到数据到来可⽤的时候, 再唤醒进程。这些设备的⽂件描述符被放在⼀个数组中, 然后select调⽤的时候遍历这个数组, 如果对于的⽂件描述符可读则会返回改⽂件描述符。 当遍历结束之后,如果仍然没有⼀个可⽤设备⽂件描述符, select让⽤户进程则会睡眠, 直到等待资源可⽤的时候在唤醒, 遍历之前那个监视的数组。 每次遍历都是依次进⾏判断的。
# -*- coding: utf-8 -*-
# 2017/11/25 22:55
# select 模拟一个socket server,注意socket必须在非阻塞情况下才能实现IO多路复用。
# 接下来通过例子了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接的。
import select
import socket
import queue server = socket.socket()
server.bind(('localhost',9000))
server.listen(1000) server.setblocking(False) # 设置成非阻塞模式,accept和recv都非阻塞
# 这里如果直接 server.accept() ,如果没有连接会报错,所以有数据才调他们
# BlockIOError:[WinError 10035] 无法立即完成一个非阻塞性套接字操作。
msg_dic = {}
inputs = [server,] # 交给内核、select检测的列表。
# 必须有一个值,让select检测,否则报错提供无效参数。
# 没有其他连接之前,自己就是个socket,自己就是个连接,检测自己。活动了说明有链接
outputs = [] # 你往里面放什么,下一次就出来了 while True:
readable, writeable, exceptional = select.select(inputs, outputs, inputs) # 定义检测
#新来连接 检测列表 异常(断开)
# 异常的也是inputs是: 检测那些连接的存在异常
print(readable,writeable,exceptional)
#[<socket.socket fd=500, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 61685)>] [] []
for r in readable:
if r is server: # 有数据,代表来了一个新连接
conn, addr = server.accept()
print("来了个新连接",addr)
inputs.append(conn) # 把连接加到检测列表里,如果这个连接活动了,就说明数据来了
# inputs = [server.conn] # 【conn】只返回活动的连接,但怎么确定是谁活动了
# 如果server活动,则来了新连接,conn活动则来数据
msg_dic[conn] = queue.Queue() # 初始化一个队列,后面存要返回给这个客户端的数据
else:
try :
data = r.recv(1024) # 注意这里是r,而不是conn,多个连接的情况
print("收到数据",data)
# r.send(data) # 不能直接发,如果客户端不收,数据就没了
msg_dic[r].put(data) # 往里面放数据
outputs.append(r) # 放入返回的连接队列里
except ConnectionResetError as e:
print("客户端断开了",r)
if r in outputs:
outputs.remove(r) #清理已断开的连接
inputs.remove(r) #清理已断开的连接
del msg_dic[r] ##清理已断开的连接 for w in writeable: # 要返回给客户端的连接列表
data_to_client = msg_dic[w].get() # 在字典里取数据
w.send(data_to_client) # 返回给客户端
outputs.remove(w) # 删除这个数据,确保下次循环的时候不返回这个已经处理完的连接了。 for e in exceptional: # 如果连接断开,删除连接相关数据
if e in outputs:
outputs.remove(e)
inputs.remove(e)
del msg_dic[e]
客户端
# -*- coding: utf-8 -*-
# 2017/11/25 22:55
import socket
client = socket.socket()
client.connect(('localhost', 9000))
while True:
cmd = input('>>> ').strip()
if len(cmd) == 0 : continue
client.send(cmd.encode('utf-8'))
data = client.recv(1024)
print(data.decode())
client.close()
优点
select⽬前⼏乎在所有的平台上⽀持, 其良好跨平台⽀持也是它的⼀个优点。
缺点
select的⼀个缺点在于单个进程能够监视的⽂件描述符的数量存在最⼤限制,在Linux上⼀般为1024, 可以通过修改宏定义甚⾄重新编译内核的⽅式提升这⼀限制, 但是这样也会造成效率的降低。⼀般来说这个数⽬和系统内存关系很⼤, 具体数⽬可以cat /proc/sys/fs/filemax察看。 32位机默认是1024个。 64位机默认是2048.对socket进⾏扫描时是依次扫描的, 即采⽤轮询的⽅法, 效率较低。当套接字⽐较多的时候, 每次select()都要通过遍历FD_SETSIZE个Socket来完成调度, 不管哪个Socket是活跃的, 都遍历⼀遍。 这会浪费很多CPU时间。
2. epoll的优点:
1. 没有最⼤并发连接的限制, 能打开的FD(指的是⽂件描述符, 通俗的理解就是套接字对应的数字编号)的上限远⼤于1024
2. 效率提升, 不是轮询的⽅式, 不会随着FD数⽬的增加效率下降。 只有活跃可⽤的FD才会调⽤callback函数; 即epoll最⼤的优点就在于它只管你“活跃”的连接, ⽽跟连接总数⽆关, 因此在实际的⽹络环境中, epoll的效率就会远远⾼于select和poll。 说明
EPOLLIN ( 可读)
EPOLLOUT ( 可写)
EPOLLET ( ET模式)
epoll对⽂件描述符的操作有两种模式: LT( level trigger) 和ET( edge trigger) 。
LT模式是默认模式, LT模式与ET模式的区别如下:
LT模式: 当epoll检测到描述符事件发⽣并将此事件通知应⽤程序, 应⽤程序可以不⽴即处理该事件
ET模式: 当epoll检测到描述符事件发⽣并将此事件通知应⽤程序, 应⽤程序必须⽴即处理
# -*- coding: utf-8 -*-
# 2017/11/26 13:54
import socket
import select
# 创建套接字
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 设置可以重复使⽤绑定的信息
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定本机信息
s.bind(("127.0.0.1",9000))
# 变为被动
s.listen(10)
# 创建⼀个epoll对象
epoll=select.epoll() # 测试, ⽤来打印套接字对应的⽂件描述符
print(s.fileno())
print(select.EPOLLIN|select.EPOLLET) # 注册事件到epoll中
# epoll.register(fd[, eventmask])
# 注意, 如果fd已经注册过, 则会发生异常
# 将创建的套接字添加到epoll的事件监听中
epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET) connections = {}
addresses = {} # 循环等待客户端的到来或者对⽅发送数据
while True:
# epoll 进⾏ fd 扫描的地⽅ -- 未指定超时时间则为阻塞等待
epoll_list=epoll.poll()
# 对事件进⾏判断
for fd,events in epoll_list:
print(fd)
print(events)
# 如果是socket创建的套接字被激活
if fd == s.fileno():
conn,addr=s.accept()
print('有新的客户端到来%s'%str(addr))
# 将 conn 和 addr 信息分别保存起来
connections[conn.fileno()] = conn
addresses[conn.fileno()] = addr
# 向 epoll 中注册 连接 socket 的 可读 事件
epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)
elif events == select.EPOLLIN:
# 从激活 fd 上接收
recvData = connections[fd].recv(1024)
if len(recvData)>0:
print('recv:%s'%recvData)
else:
# 从 epoll 中移除该 连接 fd
epoll.unregister(fd)
# server 侧主动关闭该 连接 fd
connections[fd].close()
print("%s---offline---"%str(addresses[fd]))
client
# -*- coding: utf-8 -*-
# 2017/11/25 22:55
import socket
client = socket.socket()
client.connect(('127.0.0.1', 9000))
while True:
cmd = input('>>> ').strip()
if len(cmd) == 0 : continue
client.send(cmd.encode('utf-8'))
data = client.recv(1024)
print(data.decode())
client.close()
Python网络编程篇之select和epoll的更多相关文章
- python网络编程——IO多路复用select/poll/epoll的使用
转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...
- TCP/IP网络编程之优于select的epoll(二)
基于epoll的回声服务端 在TCP/IP网络编程之优于select的epoll(一)这一章中,我们介绍了epoll的相关函数,接下来给出基于epoll的回声服务端示例. echo_epollserv ...
- Python网络编程篇之socketserver
1.socketserver模块和类 socketserver是标准库中的一个高级模块,目标是简化很多样板代码(创建网络客户端和服务器所必须的代码) 这个模块封装了socket编程所需要的各种各样的类 ...
- Python网络编程中的select 和 poll I/O复用的简单使用
首先列一下,sellect.poll.epoll三者的区别 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select ...
- TCP/IP网络编程之优于select的epoll(一)
epoll的理解及应用 select复用方法由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时接入上百个客户端.这种select方式并不适合以web服务端开发为主流的现代开发环境,所以要学习 ...
- Python网络编程篇之socket
1 socket 插座?呵呵,想多了,翻译过来意思是套接字! A network socket is an internal endpoint for sending or receiving dat ...
- python 网络编程篇
基础模拟通话网络程序: #客户端 import socket client = socket.socket() client.connect(('localhost',6969)) client.se ...
- python 网络编程 IO多路复用之epoll
python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解 此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...
- python网络编程——IO多路复用之select
1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...
随机推荐
- win10 UWP RSS阅读器
RSS简易信息聚合(也叫聚合内容)是一种RSS基于XML标准,在互联网上被广泛采用的内容包装和投递协议.RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使 ...
- mongdb单节点安装方法
mongo单节点环境安装(linux) 安装包 下载地址: (https://www.mongodb.com/download-center) 用户权限/目录 创建 dbuser用户 groupadd ...
- [转载]Vector用法(C++ Primer中文版)
转自:http://blog.sciencenet.cn/blog-261330-551086.html vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值.和 string 对 ...
- Android实现购物车功能
如图: 主要代码如下: actvity中的代码: publicclassShoppingCartActivity extendsBaseActivity { private List< ...
- js的事件循环绑定和jQuery的隐式迭代
js的事件循环绑定和jQuery的隐式迭代 js事件循环绑定 jQuery隐式迭代 先举一个例子:给定一个ul,点击列表内的每一个li元素,使它的背景色变红,下边分别用js代码和jQuery实现. & ...
- 关于github在客户端不小心删除新仓库,重建后无法上传解决方法
不小心删除了如果直接在客户端重建一个不行,首先找出本地新仓库,删除,然后在重新再客户端建立一个. 但此时如果两仓库名字一样,会发现无法上传. 此时应该在网页打开github,点击进入之前删除的仓库(云 ...
- [原创]浅谈JAVA在ACM中的应用
由于java里面有一些东西比c/c++方便(尤其是大数据高精度问题,备受广大ACMer欢迎),所以就可以灵活运用这三种来实现编程,下面是我自己在各种大牛那里总结了一些,同时加上自己平时遇到的一些jav ...
- java swing中Timer类的学习
最近在完成学校课程的java平时作业,要实现一个计时器,包含开始.暂停以及重置三个功能.由于老师规定要用这个timer类,也就去学习了一下,顺便记录一下. 首先呢去查了一下java手册上的东西,发现t ...
- hdu 6215 -- Brute Force Sorting(双向链表+队列)
题目链接 Problem Description Beerus needs to sort an array of N integers. Algorithms are not Beerus's st ...
- C++ 标准库之iomanip
C++ 标准库之iomanip istream & istream::get(char *, int, char = '\n');istream & istream::getline( ...