Python网络编程之高级篇三

  在高级篇二中,我们讲解了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对象

  

四、代码实现

 1 """
 2 利用非阻塞和epoll来实现一个服务器
 3 """
 4 import socket
 5
 6 import select
 7
 8
 9 class WebServer:
10     """定义一个web服务器"""
11
12     def __init__(self):
13         # 1.创建TCP 服务器
14         self.tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
15         # 复用端口
16         self.tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
17         # 2.绑定端口
18         self.tcp_server.bind((‘‘, 6767))
19         # 3.设为被动套接字
20         self.tcp_server.listen(128)
21
22     def run(self):
23         """运行一个服务器"""
24         #1.把服务器设置为非阻塞模式
25         self.tcp_server.setblocking(False)
26         #2.创建一个epoll对象并为服务器注册一个可接受连接的事件
27         epoll = select.epoll()
28         epoll.register(self.tcp_server.fileno(), select.EPOLLIN)
29         client_dict = dict() # 让fd与client建立关联
30         #3.服务器接受客户端的请求
31         while True:
32             # 4.监听epoll中哪个fd发生了什么事件
33             epoll_list = epoll.poll()
34             for fd, event in epoll_list:
35                 if fd == self.tcp_server.fileno():
36                     # 有客户端来连接被动套接字服务器
37                     client, addr = self.tcp_server.accept()
38                     # print(addr)
39                     # 把客户端注册到epoll中
40                     epoll.register(client.fileno(), select.EPOLLIN)
41                     # 把客户端和客户端对应的fd添加到client字典中去
42                     client_dict[client.fileno()] = client
43                 else:
44                     # 有客户端发送数据过来,但是该如何去获得这个客户端呢?
45                     data = client_dict[fd].recv(1024).decode(‘utf-8‘)
46                     if data:
47                         # 说明客户端发送数据过来了
48                         print(data)
49                         client_dict[fd].send(‘我已经收到你的数据了!\n‘.encode(‘utf-8‘))
50                     else:
51                         # 说明客户端已经关闭了
52                         client_dict[fd].close()
53
54                         client_dict.popitem()
55                         # 需要把该客户端注册的事件取消掉
56                         epoll.unregister(fd)
57
58             # 遍历client字典中每个客户端对应的fd
59             for item in client_dict.items():
60
61                 print(‘fd:{}--->addr:{}‘.format(item[0], item[1]))
62                 print(‘-‘*50)
63         # 关闭服务器
64         self.tcp_server.close()
65
66
67
68 def main():
69     #1.初始化一个TCP服务器
70     server = WebServer()
71     #2.运行一个服务器
72     server.run()
73
74
75 if __name__ == ‘__main__‘:
76     main()

原文地址:https://www.cnblogs.com/fangtaoa/p/9055985.html

时间: 2024-10-02 09:03:08

Python网络编程之高级篇三的相关文章

Python网络编程之高级篇二

在上一篇中,我们深入探讨了TCP/IP协议的11种状态,理解这些状态对我们编写服务器的时候有很大的帮助,但一般写服务器都是使用C/Java语言,因为这些语言对高并发的支持特别好.我们写的这些简单的服务器主要是为了深入学习TCP/IP协议.IO操作以及Python中协程的原理.在上一篇中也提到非阻塞这个概念,在这一篇中,我们继续深入探讨IO模型,因为理解IO操作对我们深入学习异步编程有很大帮助.所以在这一节中我们主要是从Linux内核态和用户态的层面来考虑IO操作时会发生什么样的事情,Linux内

python网络编程——socket进阶篇(select/poll/epoll)

原 生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收数据(调用recv)时也是阻塞的.原生 socket服务端在同一时刻只能处理一个客户端请求,即服务端不能同时与多个客户端进行通信,实现并发,导致服务端资源闲置(此时服务端只占据 I/O,CPU空闲). 现在的需求是:我们要让多个客户端连接至服务器端,而且服务器端需要处理来自多个客户端请求.很明显,原生socket实现不了这种需求,此时我们该采用什么方式来处理呢? 解决方法:采用I/O多路复

python 面向对象编程(高级篇)

飞机票 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个"函数"供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数 面向对象三大特性:封装.继承和多态 本篇将详细介绍Python 类的成员.成员修饰符.类的特殊成员. 类的成员 类的成员可以分为三大类:字段.方法和属性 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字

python网络编程——socket进阶篇

1 IO多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. linux中的IO多路复用     (1)select     select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作.     select目前几乎在所有的平台上支持

Python 网络编程(二)

Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端 服务端 1 2 3 4 5 6 7 8

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python 网络编程(一)

Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者. socket和file的区别: file模块是针对某个指定文件进行[打开][读写][关闭] socket模块是针对 服务器端 和 客户端Socket 进行[打开][读写][关闭] socket服务端和客户端的网

python 网络编程:socket

在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一些概念和结构,还包括一系列的协议. TCP/IP四层模型:既然有OSI七层模型,为什么我们还要定义一个TCP/IP的四层模型呢,那是因为OSI七层模型对应面过于宽泛,很多概念实现不了,也没必要实现,因此,实际生产中广泛应用的是TCP/IP四层结构,他们的对应关系如下表: TCP/IP OSI 应用层

Python网络编程06----django数据库的增删改查

首先定义model如下. class UserInfo(models.Model): username=models.CharField(max_length=50,null=True,primary_key=False,db_index=True) #可以为空,不是主键,创建索引 password=models.CharField(max_length=50,error_messages={"invalid":"出错"}) #定义出错信息 gender=model