python 简单搭建非阻塞式单进程,select模式,epoll模式服务
2017-11-02 10:01
513 查看
由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 : --> 点击这里
可以看我的上篇文章 《python 简单搭建阻塞式单进程,多进程,多线程服务》
1 单进程服务器 - 非堵塞模式
服务端 :
客户端:
我们可以看到,关键点在于for循环每个保存下来的套接字,循环请求是否有数据发来,由于速度很快,导致看起来像是多进程处理一样。
这种模式缺点在于当连接越来越多的时候,for循环轮询花费的时间越来越久,当有上千个连接,实际上却只有一个客户端有数据发来的时候,服务端依旧是不停歇的一个个轮询去问,去查找是否有请求。于是有了改进版的selec模型的服务:
2 单进程select版TCP服务器
在多路复用的模型中,较常见的有select模型和epoll模型。这两个都是系统接口,由操作系统提供。当然,Python的select模块进行了更高级的封装。网络通信被Unix系统抽象为文件件的读写,通常是一个个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回该文件描述符。当遍历结束之后,如果仍然没有这个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。
select的缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义升职重新编译内核的方式提升这个限制,但是这样也会造成效率的降低。 一般来说这个数和系统内存关系很大,具体数量可以cat /proc/sys/fs/file-max 查看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是依次扫描的,即采用轮询的⽅法,效率较低。当套接字较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都要遍历。这会浪费很多CPU时间。
3 epoll 版的服务端
epoll的好处在于:
1. 没有最大并发连接的限制,能打开的FD(指的是文件件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024
2. 效率提升,不是轮询的方式,不会随着FD数.的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远大于select和poll。
也就是说epoll是一种事件机制,不再是每次都从头开始轮询,一个个去询问是否有客户端发送数据,而是 "问" 客户端,有谁有数据吗? 这是活跃的客户端就会 “举手” ,那么epoll这个函数就会返回活跃的socket链接。
可以看我的上篇文章 《python 简单搭建阻塞式单进程,多进程,多线程服务》
1 单进程服务器 - 非堵塞模式
服务端 :
#coding=utf-8 from socket import * import time #用来存储所有的新连接的socket,这个是重点 g_socketList = [] def main(): serSocket = socket(AF_INET, SOCK_STREAM) serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1) localAddr = ('', 7788) serSocket.bind(localAddr) #这里可以适当修改listen的值来看看不同的现象 serSocket.listen(1000) #.将套接字设置为非堵塞 #.这个非常重要,设置为非堵塞后,如果accept时,没有客户端连接,那么会产生一个异常, #.所以我们用try来处理这个函数 serSocket.setblocking(False) while True: try: newClientInfo = serSocket.accept() except Exception as result: pass else: print("..新客户端连接了.:%s"%str(newClientInfo)) #这里依旧设置为非堵塞 newClientInfo[0].setblocking(False) #这里重点加入列表 g_socketList.append(newClientInfo) # 来存储需要删除的客户端信息 needDelClientInfoList = [] #这里循环读取socket列表,逐一去请求是否有客户端连接,这是select版本的简化版 #但是是理解select版本的核心 for clientSocket,clientAddr in g_socketList: try: recvData = clientSocket.recv(1024) if len(recvData)>0: print('recv[%s]:%s'%(str(clientAddr), recvData)) else: print('[%s]客户端已经关闭'%str(clientAddr)) clientSocket.close() g_needDelClientInfoList.append((clientSocket,clientAddr)) except Exception as result: pass for needDelClientInfo in needDelClientInfoList: g_socketList.remove(needDelClientInfo) if __name__ == '__main__': main()
客户端:
#coding=utf-8 from socket import * import random import time serverIp = raw_input("请输.服务器的ip:") connNum = raw_input("请输.要链接服务器的次数(例如1000):") g_socketList = [] for i in range(int(connNum)): s = socket(AF_INET, SOCK_STREAM) s.connect((serverIp, 7788)) g_socketList.append(s) print(i) while True: for s in g_socketList: s.send(str(random.randint(0,100))) # ..... time.sleep(1)
我们可以看到,关键点在于for循环每个保存下来的套接字,循环请求是否有数据发来,由于速度很快,导致看起来像是多进程处理一样。
这种模式缺点在于当连接越来越多的时候,for循环轮询花费的时间越来越久,当有上千个连接,实际上却只有一个客户端有数据发来的时候,服务端依旧是不停歇的一个个轮询去问,去查找是否有请求。于是有了改进版的selec模型的服务:
2 单进程select版TCP服务器
在多路复用的模型中,较常见的有select模型和epoll模型。这两个都是系统接口,由操作系统提供。当然,Python的select模块进行了更高级的封装。网络通信被Unix系统抽象为文件件的读写,通常是一个个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回该文件描述符。当遍历结束之后,如果仍然没有这个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。
import select import socket import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', 7788)) server.listen(5) inputs = [server, sys.stdin] running = True while True: # 调用 select 函数,阻塞等待 readable, writeable, exceptional = select.select(inputs, [], []) # 数据抵达,循环 for sock in readable: # 监听到有新的连接 if sock == server: conn, addr = server.accept() # select 监听的socket inputs.append(conn) # 监听到键盘有输入 elif sock == sys.stdin: cmd = sys.stdin.readline() running = False break # 有数据到达 else: # 读取客户端连接发送的数据 data = sock.recv(1024) if data: sock.send(data) else: # 移除select监听的socket inputs.remove(sock) sock.close() # 如果检测到用户输入敲击键盘,那么就退出 if not running: break server.close()
select的缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义升职重新编译内核的方式提升这个限制,但是这样也会造成效率的降低。 一般来说这个数和系统内存关系很大,具体数量可以cat /proc/sys/fs/file-max 查看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是依次扫描的,即采用轮询的⽅法,效率较低。当套接字较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都要遍历。这会浪费很多CPU时间。
3 epoll 版的服务端
import socket import select # 创建套接字 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 设置可以重复使.绑定的信息 s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 绑定本机信息 s.bind(("",7788)) # 变为被动 s.listen(10) # 创建.个epoll对象 epoll=select.epoll() # 将创建的套接字添加到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: # 如果是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 elif events == select.EPOLLIN: 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]))
epoll的好处在于:
1. 没有最大并发连接的限制,能打开的FD(指的是文件件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024
2. 效率提升,不是轮询的方式,不会随着FD数.的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远大于select和poll。
也就是说epoll是一种事件机制,不再是每次都从头开始轮询,一个个去询问是否有客户端发送数据,而是 "问" 客户端,有谁有数据吗? 这是活跃的客户端就会 “举手” ,那么epoll这个函数就会返回活跃的socket链接。
相关文章推荐
- python 简单搭建阻塞式单进程,多进程,多线程服务
- Python下emqtt服务的简单搭建
- [ASP.NET 设计模式] 用Visual Studio2010搭建一个简单的分层结构示例Step by Step —— 03 服务层
- [python] 简单主机批量管理工具(多进程模式)
- Python thrift rpc TProcessPoolServer 原生的多进程服务模式
- 10015---Linux IO模式及 select、poll、epoll详解
- Linux IO模式及 select、poll、epoll详解
- Python-基于数据驱动模式的自动化测试框架搭建的的逐步实现(二)
- Windows下安装Redis服务、搭建简单Redis主从复制
- 开发调试linux服务时几个好用的进程和端口查询命令组合和简单脚本(ps netstat awk grep)
- WCF学习笔记(八)服务模式下的简单事务实例和非事务实例对比
- 使用Python创建简单的HTTP服务(基于SimpleHTTPServer) 和 FTP服务(基于pyftpdlib)
- 【转帖】windows下使用gvim搭建简单的IDE编译环境(支持C/C++/Python等)
- Python创建简单的HTTP服务
- python进程简单操作
- linux网络编程并发进程,select和epoll(二)
- Linux下I/O多路复用select, poll, epoll 三种模型的Python使用
- Linux下套接字详解(八)----select模式下服务器(非阻塞,单进程+多进程+多线程)
- ELK服务搭建(开源实时日志分析ELK平台部署)(低版本—简单部署)
- python在windows下的简单搭建