什么是epoll

我们在  Python多种方式实现并发的Web Server 的最后使用单进程+单线程+非阻塞+长连接实现了一个可并发处理客户端连接的服务器。他的原理可以用以下的图来描述:

解释:

1.HTTP服务器是我们使用 单进程+单线程+非阻塞+长连接实现 的web服务器。

2.在实现的时候,我们创建了一个存放已接受Socket连接的列表,该列表是在应用程序的内存空间中的。如图中深蓝色部分

3.当有3个客户端接入的时候,列表中一共存在3个对应的socket句柄,分别对应三个小黄框。

4.灰色小框代表服务器接收请求的socket。

5.我们在进行无限循环的时候,首先是检查是否有新的客户端接入,相当于检查灰色小框是否有数据到达。然后轮询3个小黄框对应socket是否有数据到达。轮询的效率是很低的。

6.服务器在使用accept和recv时,实际上是委托操作系统帮他检查是否有数据到达,由于这个列表的socket都处于用户内存空间,所以需要将其复制到内核空间。操作系统检查完毕后,如果有数据就返回数据给应用程序,如果没有数据就以异常的方式通知应用程序。而且不光这样,操作系统可能还同时在运行其他的应用程序,这样效率会非常低。

我们再来看epoll的图:

解释:

1.我们可以看到,在结构上,最大的区别在于,存放socket的列表不处于应用程序内部。在epoll中,这个存放socket的列表处于一个特殊的内存空间,这个内存空间是应用程序与内核共享的空间。也就是说,当应用程序委托操作系统检查是否有数据到达时,无需将复制数据给内核空间,操作系统可以直接进行检查。

2.操作系统检查到某个socket有数据到达,使用事件通知的形式,直接告诉应用程序,而不是以轮询的方式。打个比方,一个厨师挨个问50个人饿了没,如果饿了就给他东西吃,这是轮询。而50个人中,谁饿了谁举手,厨师就给吃的,这叫事件通知。很明显,事件通知的效率会特别高。

实现代码:

import socket

import re
import select def handle_request(new_socket, recv_msg):
# 从请求中解析出URI
recv_lines = recv_msg.splitlines() # 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0]) if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close() response_body = html_content # 响应正确 200 OK
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "Content-Length:%d\r\n" % len(response_body)
response_header += "\r\n" response = response_header.encode("utf-8") + response_body # 返回响应数据
new_socket.send(response) def main():
# 创建TCP SOCKET实例
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# # 设置重用地址
# tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址(默认本机IP)和端口
tcp_server_socket.bind(("", 7890))
# 监听
tcp_server_socket.listen(128) # 将accept设置为非阻塞,这里设置一次,后面不管调多少次accept都是非阻塞的
tcp_server_socket.setblocking(False) # 创建一个epoll对象
epl = select.epoll()
# 将监听套接字对应的fd注册到epoll中,并让其监听有没有数据进来,所以使用EPOLLIN
epl.register(tcp_server_socket.fileno(), select.EPOLLIN) # 定义一个字典,用于存放fd和套接字的对应关系,因为操作系统在事件通知的时候,使用的是fd,而不是套接字,我们需要使用fd来找到对应
# 的套接字,从而可以调用accept和recv
fd_event_dict = dict() # 循环接收客户端连接
while True:
# 使用一个列表来接受操作系统的事件通知,poll()是阻塞的,当有数据到达时,poll才会解开阻塞
fd_event_list = epl.poll()
# 操作系统的事件通知返回一个列表(可能同时有多个套接字有数据进入),这个列表中的元素都是元组(fd,event)
for fd, event in fd_event_list:
# 首先判断事件通知中的fd是否对应监听套接字(监听套接字调用accept)
if fd == tcp_server_socket.fileno():
new_socket, client_addr = tcp_server_socket.accept()
# 监听到一个新的客户端连接,将new_socket也注册到epoll中
epl.register(new_socket.fileno(), select.EPOLLIN)
# 并且将这个socket加入fd_event_dict字段,方便以后通过fd来获取套接字
fd_event_dict[new_socket.fileno()] = new_socket
elif event == select.EPOLLIN: # 如果不是监听套接字,那么都是客户端对应的套接字
# 接收数据
recv_data = fd_event_dict[fd].recv(1024).decode("utf-8")
# 如果有数据
if recv_data:
# 处理数据
handle_request(fd_event_dict[fd], recv_data)
else: # 如果没有数据,则表示客户端断开连接
# 关闭fd对应的socket
fd_event_dict[fd].close()
# 从epoll中踢出已经断开的fd
epl.unregister(fd)
# 从字典中删除fd对应的记录
del fd_event_dict[fd] # 关闭整个SOCKET
tcp_server_socket.close() if __name__ == "__main__":
main()

解释:

1.首先创建epoll对象

2.将监听套接字对应fd注册到epoll,并设置监听数据的IN。

3.调用poll()函数,如果没有数据到达,则处于阻塞状态,如果有数据到达,则操作系统会返回一个事件通知列表。

4.遍历列表,如果发现fd是监听套接字对应fd,则使用监听套接字调用accept,并将接收到的新的客户端连接对应socket也注册到epoll中,并将其存放到字典fd_event_dict中(方便后续使用fd获取socket)。

5.如果不是监听套接字,则直接从fd_event_dict中通过fd获取对应的socket,然后调用recv来接收数据。

6.如果接收到的数据有内容,则调用请求处理逻辑。

7.如果接收到的数据为空,则表示客户端主动调用了close,想要断开连接。此时从fd_event_dict中通过fd获取对应socket,然后调用socker.close()来关闭连接。

8.关闭连接后,将该socket从epoll中剔除,并且从fd_event_dict中删除。

注意:该代码无法在windows上运行,因为epoll是Linux2.6内核增加的新功能,windows并不支持。

[Python之路] 使用epoll实现高并发HTTP服务器的更多相关文章

  1. nginx简介(轻量级开源高并发web服务器:大陆使用者百度、京东、新浪、网易、腾讯、淘宝等)(并发量5w)(一般网站apache够用了,而且稳定)

    nginx简介(轻量级开源高并发web服务器:大陆使用者百度.京东.新浪.网易.腾讯.淘宝等)(并发量5w)(一般网站apache够用了,而且稳定) 一.总结 1.在连接高并发的情况下,Nginx是A ...

  2. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

  3. Linux epoll 笔记(高并发事件处理机制)

    wiki: Epoll优点: Epoll工作流程: Epoll实现机制: epollevent; Epoll源码分析: Epoll接口: epoll_create; epoll_ctl; epoll_ ...

  4. Linux + C + Epoll实现高并发服务器(线程池 + 数据库连接池)(转)

    转自:http://blog.csdn.net/wuyuxing24/article/details/48758927 一, 背景 先说下我要实现的功能,server端一直在linux平台下面跑,当客 ...

  5. 程序员修神之路--用NOSql给高并发系统加速(送书)

    随着互联网大潮的到来,越来越多网站,应用系统需要海量数据的支撑,高并发.低延迟.高可用.高扩展等要求在传统的关系型数据库中已经得不到满足,或者说关系型数据库应对这些需求已经显得力不从心了.关系型数据库 ...

  6. 个人项目开源之c++基于epoll实现高并发游戏盒子(服务端+客户端)源代码

    正在陆续开源自己的一些项目 此为c++实现高并发的游戏盒子,平台问题需要迁移重构,所以有一些遗留问题,客户端异常断开没有处理,会导致服务器崩溃,还有基于快写代码编程平台实现的小程序切换,线程读写缓存没 ...

  7. python多进程单线程+协程实现高并发

    并发:看起来像同时运行就是并发 并行:同一时间同时被执行叫做并行,最大并行数就是CPU核数 协程不是实实在在存在的物理基础和操作系统运行逻辑,只是程序员从代码层面避开了系统对遇到IO的程序会切走CPU ...

  8. C#实现高性能高并发Socket服务器

    1.高并发服务器实现一 本文转载 转载地址 2.高并发服务器实现二 本文转载 转载内容在于学习C#实现的高并发服务器 以下个人观点 1 需要注意SocketAsyncEventArgs的使用 2 做到 ...

  9. 应用案例——高并发 WEB 服务器队列的应用

    在高并发 HTTP 反向代理服务器 Nginx 中,存在着一个跟性能息息相关的模块 - 文件缓存. 经常访问到的文件会被 nginx 从磁盘缓存到内存,这样可以极大的提高 Nginx 的并发能力,不过 ...

随机推荐

  1. ABP领域层创建实体

    原文作者:圣杰 原文地址:ABP入门系列(2)——领域层创建实体 在原文作者上进行改正,适配ABP新版本.内容相同 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进 ...

  2. GET POST请求区别

    cookie .session.tokencookie:存放在浏览器相关的硬盘文件中session:存放在服务器端的内存中,退出后,被清空token:服务器端生成后,不保存,发给客户端,客户端的hea ...

  3. drf框架的模块分析

    请求模块 请求模块是个什么鬼 ''' 1.drf的request是在wsgi的request基础上再次封装 2.wsgi的request作为drf的request一个属性:_request 3.新的r ...

  4. fio安装使用

    # wget http://brick.kernel.dk/snaps/fio-2.2.5.tar.gz # tar xvf fio-2.2.5.tar.gz# cd fio-2.2.5# ./con ...

  5. PB赋值粘贴

    复制:string ls_templs_temp = trim(sle_1.text)Clipboard(ls_temp) 粘贴:string ls_templs_temp = Clipboard() ...

  6. Jmeter之正则表达式取样器~案例详解

    正则:按照规则提取数据 场景:A请求获得的响应数据,需要作为B请求的提交数据(eg:A:购物车页面→B:下单(正则提取购物车的商品信息,数量信息等)) 常用正则表达式:(.+?) 辅助:添加Debug ...

  7. 进阶Java编程(13)反射与Annotation

    1,反射取得Annotation信息 从JDK1.5之后Java提供了Annotation技术支持,这种技术为项目的编写带来了新的模型,而后经过了十年的发展,Annotation的技术得到了非常广泛的 ...

  8. No compiler is provided in this environment. Perhaps you are running on a JR

    maven编译项目时出错,提示信息如下: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3 ...

  9. java中成员变量和局部变量在内存中的分配

    对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量:局部变量就是方法或语句块内部定义的变量.局部变量必须初始化. 形式参数是局部变量,局部变量中基础数据类型的引用和值都存储在栈中,对象引 ...

  10. BootStrape基础使用

    官网:www.bootcss.com 一. 全局css样式 栅格系统 栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局 <!DOCTYPE html> < ...