selectors模块
  •   此模块允许基于选择模块原语构建高级别和高效的I / O多路复用。
  •   鼓励用户使用此模块,除非他们想要精确控制使用的os级别的原语。

注:selectors也是包装了select高级的包装内置函数,它包装了select与epoll,优先使用epoll windos内只支持select。

实现接收上万并发
服务端:
import selectors
import socket # 生成select实例对象
sel = selectors.DefaultSelector() def accept(sock, mask): # 接收链接
conn, addr = sock.accept() # Should be ready
print('accepted', conn, 'from', addr) # 链接设置非阻塞模式
conn.setblocking(False) # 注册conn,回调 read函数
sel.register(conn, selectors.EVENT_READ, read) def read(conn, mask):
data = conn.recv(1024) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data) # Hope it won't block
else:
print('closing', conn) # 取消注册
sel.unregister(conn) # 关闭链接
conn.close() sock = socket.socket()
sock.bind(('localhost', 9999))
sock.listen(10000)
sock.setblocking(False) # 注册server事件:
# 参数一:sock 进行监听
# 参数二:selectors.EVENT_READ 执行动作
# 参数三:accept,只要来一个链接就回调这个函数
sel.register(sock, selectors.EVENT_READ, accept) # 第一次调用server,register accept
# 第二次调用client,register read
while True: # 调用select:优先使用epoll
events = sel.select() # 只要不阻塞就有调用的数据,返回一个列表
# 默认阻塞,有活动链接就返回活动的链接列表
for key, mask in events: # callback相当于调accept函数
callback = key.data # 获取函数内存地址,加入参数
# key.fileobj = 文件句柄
callback(key.fileobj, mask)

客户端:

import socket
import sys messages = [ b'This is the message. ',
b'It will be sent ',
b'in parts.',
] # 传入链接参数
server_address = ('localhost', 9999) # Create a TCP/IP socket
# 使用列表生成式,生成多个请求。
# Winuds使用select支持并发并不多 这里测试40个并发
# Linux默认使用epoll可支持上万并发可修改10000
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM)
for i in range(40)] print('connecting to %s port %s' % server_address) # 循环socks连续链接4次
for s in socks: s.connect(server_address) # 循环发送数据
for message in messages: # Send messages on both sockets
# 每个客户端依次发送数据
for s in socks:
print('%s: sending "%s"' % (s.getsockname(), message) )
s.send(message) # Read responses on both sockets
# 服务端收数据
for s in socks:
data = s.recv(1024) # getsockname()服务端返回
print( '%s: received "%s"' % (s.getsockname(), data) ) # 没有数据打印客户端要关闭了
if not data:
print(sys.stderr, 'closing socket', s.getsockname() )
注:建立一个socket就需要建立一个文件句柄,最大数65535
注:Linux修改文件描述符数值 当前执行
ulimit -SHn 65535 #修改链接端口
ulimit -n #查看链接端口

基于Linux下 需要修改配置

epoll实现多并发socket

import socket, logging
import select, errno logger = logging.getLogger("network-server") def InitLog():
logger.setLevel(logging.DEBUG) fh = logging.FileHandler("network-server.log")
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
fh.setFormatter(formatter) logger.addHandler(fh)
logger.addHandler(ch) if __name__ == "__main__":
InitLog() try:
# 创建 TCP socket 作为监听 socket
listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
except socket.error as msg:
logger.error("create socket failed") try:
# 设置 SO_REUSEADDR 选项
listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error as msg:
logger.error("setsocketopt SO_REUSEADDR failed") try:
# 进行 bind -- 此处未指定 ip 地址,即 bind 了全部网卡 ip 上
listen_fd.bind(('', 2003))
except socket.error as msg:
logger.error("bind failed") try:
# 设置 listen 的 backlog 数
listen_fd.listen(10)
except socket.error as msg:
logger.error(msg) try:
# 创建 epoll 句柄
epoll_fd = select.epoll()
# 向 epoll 句柄中注册 监听 socket 的 可读 事件
epoll_fd.register(listen_fd.fileno(), select.EPOLLIN)
except select.error as msg:
logger.error(msg) connections = {}
addresses = {}
datalist = {}
while True:
# epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待
epoll_list = epoll_fd.poll() for fd, events in epoll_list:
# 若为监听 fd 被激活
if fd == listen_fd.fileno():
# 进行 accept -- 获得连接上来 client 的 ip 和 port,以及 socket 句柄
conn, addr = listen_fd.accept()
logger.debug("accept connection from %s, %d, fd = %d" % (addr[0], addr[1], conn.fileno()))
# 将连接 socket 设置为 非阻塞
conn.setblocking(0)
# 向 epoll 句柄中注册 连接 socket 的 可读 事件
epoll_fd.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)
# 将 conn 和 addr 信息分别保存起来
connections[conn.fileno()] = conn
addresses[conn.fileno()] = addr
elif select.EPOLLIN & events:
# 有 可读 事件激活
datas = ''
while True:
try:
# 从激活 fd 上 recv 10 字节数据
data = connections[fd].recv(10)
# 若当前没有接收到数据,并且之前的累计数据也没有
if not data and not datas:
# 从 epoll 句柄中移除该 连接 fd
epoll_fd.unregister(fd)
# server 侧主动关闭该 连接 fd
connections[fd].close()
logger.debug("%s, %d closed" % (addresses[fd][0], addresses[fd][1]))
break
else:
# 将接收到的数据拼接保存在 datas 中
datas += data
except socket.error as msg:
# 在 非阻塞 socket 上进行 recv 需要处理 读穿 的情况
# 这里实际上是利用 读穿 出 异常 的方式跳到这里进行后续处理
if msg.errno == errno.EAGAIN:
logger.debug("%s receive %s" % (fd, datas))
# 将已接收数据保存起来
datalist[fd] = datas
# 更新 epoll 句柄中连接d 注册事件为 可写
epoll_fd.modify(fd, select.EPOLLET | select.EPOLLOUT)
break
else:
# 出错处理
epoll_fd.unregister(fd)
connections[fd].close()
logger.error(msg)
break
elif select.EPOLLHUP & events:
# 有 HUP 事件激活
epoll_fd.unregister(fd)
connections[fd].close()
logger.debug("%s, %d closed" % (addresses[fd][0], addresses[fd][1]))
elif select.EPOLLOUT & events:
# 有 可写 事件激活
sendLen = 0
# 通过 while 循环确保将 buf 中的数据全部发送出去
while True:
# 将之前收到的数据发回 client -- 通过 sendLen 来控制发送位置
sendLen += connections[fd].send(datalist[fd][sendLen:])
# 在全部发送完毕后退出 while 循环
if sendLen == len(datalist[fd]):
break
# 更新 epoll 句柄中连接 fd 注册事件为 可读
epoll_fd.modify(fd, select.EPOLLIN | select.EPOLLET)
else:
# 其他 epoll 事件不进行处理
continue

Python selectors实现socket并发的更多相关文章

  1. Python select实现socket并发

    Python select  Python的select()方法直接调用操作系统的IO接口,它监控sockets,open files, and pipes(所有带fileno()方法的文件句柄)何时 ...

  2. python全栈开发从入门到放弃之socket并发编程之协程

    一.为什么会有协程 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情 ...

  3. selectors实现高并发

    1. 下面的例子,客户端给服务端发送消息,服务端把消息返回 server #!/usr/bin/env python import selectors import socket import tim ...

  4. python之爬虫_并发(串行、多线程、多进程、异步IO)

    并发 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢 import requests def fetch_async(url): res ...

  5. python笔记-10(socket提升、paramiko、线程、进程、协程、同步IO、异步IO)

    一.socket提升 1.熟悉socket.socket()中的省略部分 socket.socket(AF.INET,socket.SOCK_STREAM) 2.send与recv发送大文件时对于黏包 ...

  6. 老男孩Day11作业:selectors版socket

    一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP 允许多用户并发上传下载文件 二.readme 一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP ...

  7. SELECTORS模块实现并发简单版FTP

    环境:windows, python 3.5功能:使用SELECTORS模块实现并发简单版FTP允许多用户并发上传下载文件 结构:ftp_client ---| bin ---| start_clie ...

  8. selectors版socket

    一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP 允许多用户并发上传下载文件 二.readme 一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP ...

  9. Python/ selectors模块及队列

    Python/selectors模块及队列 selectors模块是可以实现IO多路复用机制: 它具有根据平台选出最佳的IO多路机制,比如在win的系统上他默认的是select模式而在linux上它默 ...

随机推荐

  1. linux系统下Nagios+rrdtool+Pnp4nagios监控环境的搭建

    环境中的软件版本>>>>>>>>>>>>>>>>>>nagios版本:3.5.1rrdtoo ...

  2. Spring AOP @AspectJ进阶

    @AspectJ可以使用切点函数定义切点,我们还可以使用逻辑运算符对切点进行复核运算得到复合的切点,为了在切面中重用切点,我们还可以对切点进行命名,以便在其他的地方引用定义过的切点.当一个连接点匹配多 ...

  3. mousedown、mousemove、mouseup和touchstart、touchmove、touchend

    拖动时候用到的三个事件:mousedown.mousemove.mouseup在移动端都不起任何作用.毕竟移动端是没有鼠标的,查资料后发现,在移动端与之相对应的分别是:touchstart.touch ...

  4. Linux wildcard

    Linux中的通配符: 需要注意的是正则表达式与通配符完全是两个东西.wildcard代表的是bash操作接口的一个功能,而正则表达式是一种字符串处理的方法. 例如,'?',在通配符中表示一个字符,在 ...

  5. EF Unknown column 'Project1.FamilyMembers_ID' in 'field list'

    产生成的查询语句中有:Project1.FamilyMembers_ID 字段 原因:就是对象主键对象中有一个属性!

  6. office 2016密钥

    Office 2016 Pro Plus Retail 版激活密钥:[Key]:3XJTG-YNBMY-TBH9M-CWB2Y-YWRHH[Key]:6TCQ3-NBBJ2-RTJCM-HFRKV-G ...

  7. luogu3830 [SHOI2012]随机树

    传送门:洛谷 题目大意:对于一个只有一个节点的二叉树,一次操作随机将这棵树的叶节点的下方增加两个节点.$n-1$次操作后变为$n$个叶节点的二叉树.求:(1)叶节点平均深度的期望值(2)树深度的数学期 ...

  8. 高性能Nginx服务器-反向代理

    Nginx Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.由俄罗斯的程序设计师Igor Sysoev所开发,供 ...

  9. 浅谈Java中的关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...

  10. java之Spring集成CXF简单调用

    简介 Apache CXF = Celtix + XFire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF 了,以下简称为 CXF.CXF 继承了 Celtix 和 X ...