• 6.poll
  • poll解决了套接字有上限的问题,效率和select一样,都是轮询方式。
  • 7.单进程tcp服务器 epoll版 (windows不支持)
  • epoll-->解决支持上限问题-->采用的是事件通知(效率高)
  • 代码
  • import select
  • from socket import *
  • print(dir(select))
  • server_socket=socket(AF_INET,SOCK_STREAM)
  • server_socket.bind(("",9999))
  • server_socket.listen(5)
  • epol=epoll()  # 创建epoll对象
  • epol.register(server_socket.fileno(),EPOLLIN|EPOLLET) # 注册事件
  • socket_list={}  # 装socket的列表
  • socket_addr={}  #装socket的地址
  • while True:
  • print("-------1111111-------")
  • epoll_list=epol.poll()  # [(fd,事件),(),(),()]
  • print("--------22222------")
  • for fd,event in epoll_list:
  • # 有新的连接
  • if fd==server_socket.fileno():
  • new_socket,new_addr=server_socket.accept()
  • #\往子典中添加数据
  • socket_list[new_socket.fileno()]=new_socket
  • socket_addr[new_socket.fileno()]=new_addr
  • # 注册事件
  • epol.register(new_socket.fileno(),EPOLLIN|EPOLLET)
  • elif event==EPOLLIN:
  • print("哈哈哈!收到数据了")
  • new_socket=socket_list[fd]
  • new_addr=socket_addr[fd]
  • # 读取数据
  • content=new_socket.recv(1024)
  • if len(content)>0:
  • print("收到数据是:",content.decode("utf-8"))
  • else:
  • epol.unregister(fd)  # 取消注册
  • new_socket.close()
  • print(new_addr,"下线了....")
  • 优点:
  • 1.没有最大并发连接的限制,能打开的FD(指的是文件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024。
  • 2.epoll采用的事件通知机制,效率高,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,
  • 而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。
  • 8.拓展-文件描述符
  • 1.python中fileno() 方法返回一个整型的文件描述符(file descriptor)  打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
  • 2.它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,会给这个文件指定一个文件描述符
  • 3.习惯上,标准输入sys.stdin.fileno() 的文件描述符为 0 标准输出sys.stdout.fileno() 的文件描述符为 1  标准错误sys.stderr.fileno() 的文件描述符为 2
  • 9.协程
  • 1.进程,线程,协程(微线程)
  • 2.协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时。协程,则只使用一个线程,分解一个线程成为多个“微线程”,在一个线程中规定某个代码块的执行顺序。
  • 3.协程的应用场景:当程序中存在大量不需要CPU的操作时(IO),线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,
  • 4.协程-greenlet-手动切换
  • from greenlet import greenlet
  • g1=greenlet(协程任务函数名)  创建greennlet对象
  • g1.switch()  执行协程并切换
  • 使用多个greenlet对象实现多个协程之间的手动切换
  • 5.协程-gevent-自动切换
  • import gevent
  • g1=gevent.spawn(协程任务函数名,参数)  创建gevent 对象 并执行
  • g1.join() 让线程等待协程执行完毕,否则没机会执行
  • 正常开发中,遇到好事操作自动切换协程,(可以在协程中使用gevent.sleep(1)手动搭建耗时操作)
  • 也可以使用 gevent.joinall([协程对象1,协程对像2,协程对象n])
  • gevent.getcurrent() 返回协程对象
  • 并发下载器
  • from gevent import monkey  
  • monkey.patch_all()#猴子补丁,将标准库中涉及的IO操作换成gevent的
  • import gevent
  • import urllib.request
  • #下载地址
  • def my_download(url):
  • print("GET url==%s" % url)
  • result = urllib.request.urlopen(url)
  • data = result.read()
  • print("url==%s,data is len=%s" % (url,len(data)))
  • gevent.joinall([
  • gevent.spawn(my_download,"https://www.baidu.com"),
  • gevent.spawn(my_download,"http://www.atguigu.com"),
  • gevent.spawn(my_download,"https://github.com"),
  • gevent.spawn(my_download,"http://www.atguigu.com/images/logo.gif"),
  • ])
  • 10.单进程tcp服务器 gevent 版
  • from gevent import monkey,socket,spawn
  • monkey.path_all()    #把标准库中的socket等给替换掉.
  • def server(new_socket):
  • while True:
  • content=new_socket.recv(1024)
  • if len(content)==0:
  • print("客户端下线")
  • break
  • print("收到消息:%s"%content.decode("utf-8"))
  • if __name__=="__main__":
  • server_socket=socket.socket()  #使用gevent中的socket
  • server_socket.bind(("",9999))
  • server.listen(5)
  • while True:
  • new_socket,new_addr=server.socket.accept()
  • spawn(server,new_socket)