web服务-3、epoll高效率实现并发服务器
知识点:
之前写的四种方法实现并发服务效率都还是低,早期的服务器采用的是select和poll方式,select这种方式的特点是轮询所有套接字去一个个看有没有事件发生,但是装套接字的列表长度是有限制的,而且轮询方式效率低,poll方式只是解决了装套接字这个列表的长度限制,但也是采用轮询的方式。目前实际场景中,linux服务器采用的都是epoll方案实现并发服务的,它解决了轮询这种低效率方式,采用事件通知的方式告诉应用程序哪个套接字发来了数据它的效率远远高于其他四种,它也是采用单进程单线程的方式,但是是堵塞的,主要有俩大特点:1、epoll方式会映射一个和操作系统一样的能处理应用程序的指定的文件描述符内存空间(即省去了将应用程序中的套接字文件描述符拷贝到操作系统中的过程),它既不属于应用程序也不属于操作系统 ,是俩个共用的的2、客户端套接字发送数据,epoll采用事件通知方式告诉应用程序,而不需要轮询所有的套接字列表
1、代码实现:
#!/usr/bin/env python # coding=utf-8 # author:刘仲 # datetime:2018/8/6 9:24 # software: PyCharm import socket import select import re """之前写的四种方法实现并发服务效率都还是低,早期的服务器采用的是select和poll方式,select这种方式的特点是轮询所有套接字去 一个个看有没有事件发生,但是装套接字的列表是有限制的,而且轮询方式效率低,poll方式只是解决了装套接字这个列表的长度限制, 但也是采用轮询的方式。目前实际场景中,linux服务器采用的都是epoll方案实现并发服务的,它解决了轮询这种低效率方式,采用事件 通知的方式告诉应用程序哪个套接字发来了数据它的效率远远高于其他四种,它也是采用单进程单线程的方式,但是是堵塞的,主要有俩 大特点:1、epoll方式会映射一个和内核一样的能处理应用程序的事件的2、客户端套接字发送数据,内核的文件系统会采用事件通知的 方式告诉应用程序 """ def webserver(): web_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) web_socket.bind(('127.0.0.1', 7786)) web_socket.listen(128) epl = select.epoll() # 创建epoll对象 epl.register(web_socket.fileno(), select.EPOLLIN | select.EPOLLET) # 将监听套接字注册到epl fd_dict = dict() # 存放新的套接字的文件描述符和与之对应的套接字对象 while True: epl_list = epl.poll() # 默认堵塞,获取注册到epl对象中的所有套接字列表,列表里存的是元祖,元祖俩个值分别对应的是 # 套接字的文件描述符和事件类型 for fd, event in epl_list: # 获取套接字的文件描述符和事件类型 if fd == web_socket.fileno(): # 刚开始epl_list肯定存放的是监听套接字,所以新的客户端链接进来,先判断文件描述符是否等与监听套接字的文件描述符 new_socket, new_adress = web_socket.accept() # 新客户端链接进来进行解堵塞 fd_dict[new_socket.fileno()] = new_socket # 将新的客户端套接字的文件描述符和与之对应的套接字存放到字典中 epl.register(new_socket.fileno(), select.EPOLLIN) # 将新的套接字注册到epl中 elif event == select.EPOLLIN: # 当监听事件触发,即客户端发来数据 try: data = fd_dict[fd].recv(1024).decode('utf-8') except: print('客户端没有发送数据') else: if data: data1 = data.splitlines() request_url = re.match(r"[^/]+(/[^ ]*)",data1[0]) if request_url: file_name = request_url.group(1) if file_name == '/': file_name = '/index.html' try: body = open('./html'+file_name, 'rb') except: response_head = 'HTTP 404 NOT FOUND/1.1\r\n' response_head += '\r\n' response_body = '<h1>NOT FOUND PAGE</H1>' fd_dict[fd].send((response_head + response_body).encode('utf-8')) else: response_body = body.read() body.close() response_head = 'HTTP 200 OK/1.1\r\n' response_head += 'Content-Length:%d\r\n'%len(response_body) response_head += '\r\n' fd_dict[fd].send(response_head.encode('utf-8')+response_body) fd_dict[fd].close() else: fd_dict[fd].close() # 关闭新的套接字 epl.unregister(fd) # 从epl中移除该文件描述符的链接 del fd_dict[fd] # 删除这个套接字的键值对 web_socket.close() if __name__ == '__main__': webserver()