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的更多相关文章

  1. python网络编程——IO多路复用select/poll/epoll的使用

    转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...

  2. TCP/IP网络编程之优于select的epoll(二)

    基于epoll的回声服务端 在TCP/IP网络编程之优于select的epoll(一)这一章中,我们介绍了epoll的相关函数,接下来给出基于epoll的回声服务端示例. echo_epollserv ...

  3. Python网络编程篇之socketserver

    1.socketserver模块和类 socketserver是标准库中的一个高级模块,目标是简化很多样板代码(创建网络客户端和服务器所必须的代码) 这个模块封装了socket编程所需要的各种各样的类 ...

  4. Python网络编程中的select 和 poll I/O复用的简单使用

    首先列一下,sellect.poll.epoll三者的区别 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select ...

  5. TCP/IP网络编程之优于select的epoll(一)

    epoll的理解及应用 select复用方法由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时接入上百个客户端.这种select方式并不适合以web服务端开发为主流的现代开发环境,所以要学习 ...

  6. Python网络编程篇之socket

    1 socket 插座?呵呵,想多了,翻译过来意思是套接字! A network socket is an internal endpoint for sending or receiving dat ...

  7. python 网络编程篇

    基础模拟通话网络程序: #客户端 import socket client = socket.socket() client.connect(('localhost',6969)) client.se ...

  8. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

  9. python网络编程——IO多路复用之select

    1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...

随机推荐

  1. 【计算机网络基础】数据交换技术和多路复用技术的正(nao)确(can)打开方式

    交换的作用   数据交换是计算机网络中两个终端进行数据传输的方式,它又可以分成两种类型:电路交换和分组交换.很显然,问题的核心在于“交换”,那么我们首先要思考的是:交换的作用是什么?   “交换”的作 ...

  2. HTML之头部标签

    <header> head 元素元素包含了所有的头部标签元素 可以添加在头部区域的元素标签为: title,style,meta,link,script,noscript,base < ...

  3. 张高兴的 Windows 10 IoT 开发笔记:使用 ULN2003A 控制步进电机

    GitHub:https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/ULN2003A

  4. PHP之回调函数传参(解决eval函数拼接对象参数的问题)

    在使用Smarty时,定义了一个统一调用控制器的函数,如下: function C($name, $method){//控制器的名称和其中方法的名称 require_once "contro ...

  5. vim搭建笔记

    在接触vim近一年后,自己的vimrc都是拼凑别人的,所以有很多插件和配置并不会使用 现在,我决定,花费一天时间,一步一步的搭建自己的vim配置! 去该网址下载安装vim http://www.vim ...

  6. 461. Hamming Distance(leetcode)

    The Hamming distance between two integers is the number of positions at which the corresponding bits ...

  7. LeetCode 339. Nested List Weight Sum (嵌套列表重和)$

    Given a nested list of integers, return the sum of all integers in the list weighted by their depth. ...

  8. 初识.Net IL

    1.IL基本资料 1.IL概述 IL是.NET框架中中间语言(Intermediate Language)的缩写.使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出 ...

  9. 2.动手实操Apache ZooKeeper

    Tips 做一个终身学习的人! 日拱一卒,功不唐捐. 在本节中,我们将讲解如何下载并安装Apache ZooKeeper,以便我们可以直接开始使用ZooKeeper. 本部分旨在通过提供详细的安装和使 ...

  10. Input文本框属性及js

    <input id="txt_uname" maxlength="16" onblur="validata()" onkeyup=&q ...