在高级篇二中,我们讲解了5中常用的IO模型,理解这些常用的IO模型,对于编写服务器程序有很大的帮助,可以提高我们的并发速度!因为在网络中通信主要的部分就是IO操作。在这一篇当中我们会重点讲解在第二篇当中提到的IO复用模型,即select机制。其实select机制有一些缺陷,后来产生了一种更加高效的机制epoll,稍后会讲解!

一、select机制

  1. 原理:select可以理解成一个监听器,可以监听多个文件描述符。当某个文件描述符的状态发生改变了(可读/可写),操作系统就会发送消息给应用程序,去处理数据。

  2. 优点:几乎所有平台都支持,跨平台支持性较好。

  3. 缺点:

    (1). 当个进程/线程可监视的文件描述符数量有限制。

    (2). 对文件描述符的扫描是线性的,采用轮询的方式,每次都是从头一直扫描到结尾,当文件描述符的列表变大时,会相当浪费时间和CPU

    (3). 把包含大量文件描述符的数组从内核空间拷贝到用户空间,当数组小的时候可能还好,但是随着数组的增大会变得很浪费资源。

  4. 水平触发:

    当select()把状态发生变化的文件描述符报告给进程之后,如果进程没有进行任何处理,那么下次select()还会报告这些文件描述符。

二、epoll

  epoll可以看成是select/poll(本质就是select)的加强版,打破了很多select的约束,以及添加了一些其他的功能!

  1. 为什么epoll效率很高呢?

    epoll最大的特点是只告诉服务器有哪些文件描述符(fd)发生了变化。如果服务器不去处理相应的fd,那么操作系统就会把这个fd丢弃,不再给服务器发送消息(边缘触发)!除此之外,epoll是采用事件监听的方式通知,这也是epoll的魅力所在!

  2.原理:

    

    (1). 注册在epoll中的文件描述符,操作系统的事件监听会去监听文件描述符集合(fd_set)

    (2). 如果有fd发生了变化,那么事件监听会向操作系统报告发生变化的fd

    (3). 操作系统会给服务器发送消息,通知它你关注的fd有变化,去处理吧

    (4). 此时服务器就去共享内存中读取数据了!

  3. 优点:

    (1). 没有最大连接数的限制。

    (2). 不采用轮询的方式去处理fd,而是采用事件监听的方式,即哪个fd有事件发生,OS通知服务器使用相应的回调函数来处理fd

    (3). 内存拷贝:当有数据到来时,操作系统会给服务器发送通知去处理数据。通过采用共享内存的方式加快用户空间与内核空间消息的传递速度。

  4. 误区:

    并不是在任何情况下,epoll都要比select/poll高效,只有当很多连接请求到来时才会很高效!

三、epoll编程模型

  (1). 创建1个epoll对象

  (2). 告诉epoll对象,在指定的fd上监听指定的事件

  (3). 询问epoll对象,自从上次查询后,哪些fd上发生了哪些事件  

  (4). 在这些fd上执行一些操作

  (5). 告诉epoll对象,修改fd列表或注册事件,并监控

  (6). 重复步骤3-5,直到完成

  (7). 销毁epoll对象

  

四、代码实现

 """
利用非阻塞和epoll来实现一个服务器
"""
import socket import select class WebServer:
"""定义一个web服务器""" def __init__(self):
# 1.创建TCP 服务器
self.tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 复用端口
self.tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2.绑定端口
self.tcp_server.bind(('', 6767))
# 3.设为被动套接字
self.tcp_server.listen(128) def run(self):
"""运行一个服务器"""
#1.把服务器设置为非阻塞模式
self.tcp_server.setblocking(False)
#2.创建一个epoll对象并为服务器注册一个可接受连接的事件
epoll = select.epoll()
epoll.register(self.tcp_server.fileno(), select.EPOLLIN)
client_dict = dict() # 让fd与client建立关联
#3.服务器接受客户端的请求
while True:
# 4.监听epoll中哪个fd发生了什么事件
epoll_list = epoll.poll()
for fd, event in epoll_list:
if fd == self.tcp_server.fileno():
# 有客户端来连接被动套接字服务器
client, addr = self.tcp_server.accept()
# print(addr)
# 把客户端注册到epoll中
epoll.register(client.fileno(), select.EPOLLIN)
# 把客户端和客户端对应的fd添加到client字典中去
client_dict[client.fileno()] = client
else:
# 有客户端发送数据过来,但是该如何去获得这个客户端呢?
data = client_dict[fd].recv(1024).decode('utf-8')
if data:
# 说明客户端发送数据过来了
print(data)
client_dict[fd].send('我已经收到你的数据了!\n'.encode('utf-8'))
else:
# 说明客户端已经关闭了
client_dict[fd].close() client_dict.popitem()
# 需要把该客户端注册的事件取消掉
epoll.unregister(fd) # 遍历client字典中每个客户端对应的fd
for item in client_dict.items(): print('fd:{}--->addr:{}'.format(item[0], item[1]))
print('-'*50)
# 关闭服务器
self.tcp_server.close() def main():
#1.初始化一个TCP服务器
server = WebServer()
#2.运行一个服务器
server.run() if __name__ == '__main__':
main()

Python高级网络编程系列之第三篇的更多相关文章

  1. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

  2. Python高级网络编程系列之第二篇

    在上一篇中,我们深入探讨了TCP/IP协议的11种状态,理解这些状态对我们编写服务器的时候有很大的帮助,但一般写服务器都是使用C/Java语言,因为这些语言对高并发的支持特别好.我们写的这些简单的服务 ...

  3. Python高级网络编程系列之基础篇

    一.Socket简介 1.不同电脑上的进程如何通信? 进程间通信的首要问题是如何找到目标进程,也就是操作系统是如何唯一标识一个进程的! 在一台电脑上是只通过进程号PID,但在网络中是行不通的,因为每台 ...

  4. Python高级网络编程系列之终极篇---自己实现一个Web框架

    通过前面几个小节的学习,现在我们想要把之前学到的知识点给串联起来,实现一个很小型的Web框架.虽然很小,但是用到的知识点都是比较多的.如Socket编程,装饰器传参在实际项目中如何使用.通过这一节的学 ...

  5. 《安卓网络编程》之第三篇 使用Apache接口

    在Android系统中,提供了一下三种通信接口: 标准的Java 接口:java.net Apache接口:org.apache.http Android网络接口:android.net.http 在 ...

  6. 猫哥网络编程系列:HTTP PEM 万能调试法

    注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...

  7. python之网络编程

    本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...

  8. python基础网络编程--转

    python之网络编程 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的 ...

  9. 猫哥网络编程系列:详解 BAT 面试题

    从产品上线前的接口开发和调试,到上线后的 bug 定位.性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期.不论你是前后端的开发岗位,还是 SQA.运维等其他技术岗位,掌握网络编程知识均是岗位的 ...

随机推荐

  1. JavaScript之使用AJAX(适合初学者)

      网上关于AJAX的教程和分享层出不穷,现实生活中关于AJAX的书籍也是琳琅满目,然而太多的选择容易令人眼花缭乱,不好取舍.事实是,一般的教程或书籍都不会讲Web服务器的搭建,因此,对于初学者(比如 ...

  2. MyBatis(国税)

    一.MyBatis概要 1.1.ORM介绍 对象关系映射(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统 ...

  3. win10 关闭自动更新

    方法一 : 利用组策略关闭win10自动更新的步骤如下:1.按win+R打开“运行”,输入“gpedit.msc”,按下回车. 2.找到“计算机配置”→““管理模板”→“Windows 组件”→“Wi ...

  4. C# 循环语句 for

    循环:反复执行某段代码. 循环四要素:初始条件,循环条件,循环体,状态改变. for格式 for(初始条件;循环条件;状态改变) { 循环体 } break ——中断循环,跳出整个循环 continu ...

  5. PPT文件流转为图片,并压缩成ZIP文件输出到指定目录

    实现流程: 接收InputStream流->复制流->InputStream流转为PPT->PPT转为图片->所有图片压缩到一个压缩文件下 注意: 1.PPT文件分为2003和 ...

  6. 【Dubbo&&Zookeeper】6、 给dubbo接口添加白名单——dubbo Filter的使用

    在开发中,有时候需要限制访问的权限,白名单就是一种方法.对于Java Web应用,Spring的拦截器可以拦截Web接口的调用:而对于dubbo接口,Spring的拦截器就不管用了. dubbo提供了 ...

  7. Visual Flow 简介

    Visual Flow(流) Salesforce提供了几种自动化流程工具,其中的Visual Flow(流)可以用来实现用户界面和逻辑,并对数据进行CRUD(Create 创建,Read 读取,Up ...

  8. loadrunner 场景设计-集合点设置

    场景设计-集合点设置 by:授客 QQ:1033553122 1  作用 通过让多用户在同一时间点上进行并发操作来测试系统的并发处理的能力 2  实现 通过集合点函数来实现. 注意:集合点经常和事务结 ...

  9. JavaScript大杂烩12 - 理解Ajax

    AJAX缘由 再次谈起这个话题,我深深的记得就在前几年,AJAX被炒的如火如荼,就好像不懂AJAX,就不会Web开发一样.要理解AJAX为什么会出现,就要先了解Web开发面临的问题. 我们先来回忆一下 ...

  10. 从零自学Java-10.充分利用现有对象

    1.超类和子类的设计:2.建立继承层次:3.覆盖方法. 程序StringLister:使用数组列表和特殊的for循环将一系列字符串按字母顺序显示到屏幕上.这些字符串来自一个数组和命令行参数 packa ...