epoll是事件通知方式接收数据,效率比轮询要高
代码:
1 import socket 2 import re 3 import select 4 5 6 def client_server(new_client,recv_data): 7 # 接收客户端请求 8 # recv_data = new_client.recv(1024).decode("utf-8") 9 # 把读出的数据分行 10 recv_data_lines = recv_data.splitlines() 11 # 正则匹配GET后面的请求的页面 12 # [^/]表示非“/”字符 +表示至少1个或多个,[^ ]只要不是空格,*表示0个或多个 13 ret = re.match(r"[^/]+(/[^ ]*)",recv_data_lines[0]) 14 15 # ret = re.search(r"/[^ ]*",recv_data_lines[0]) # 查找第一次“/”后面非空格的字符串 16 file_name = ret.group(1) 17 # 打开文件 18 try: 19 f = open("./html" + file_name,"rb") 20 except: 21 # 创建返回数据 HEADER 22 response = "HTTP/1.1 404 NOT FOUND\r\n" 23 response += "\r\n" 24 response += "-----NOT FOUND----" 25 # 发送数据到客户端 26 new_client.send(response.encode("utf-8")) 27 else: 28 # 读出数据 29 html_content = f.read() 30 # 把读出的数据放到BODY体里面 31 response_body = html_content 32 33 # 创建返回数据 HEADER 34 response_header = "HTTP/1.1 200 ok\r\n" 35 # 告诉浏览器发送过去的长度 36 response_header += "Content-Length:%d\r\n" % len(response_body) 37 response_header += "\r\n" 38 # 把HEADER和BODY拼接在一起 39 response = response_header.encode("utf-8") + response_body 40 # 发送数据到客户端 41 new_client.send(response) 42 43 # 关闭套接字 长链接不要关闭此套接字 等客户端关闭 44 # new_client.close() 45 46 47 def main(): 48 # 创建套接字 49 tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 50 # 设置当服务器先调用close() 即服务器4次回收之后资源能够立即释放,这样就保证了下次运行程序时,可以立即使用 51 # SO_REUSEADDR重复使用地址 52 tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 53 # 绑定端口 54 tcp_server_socket.bind(("",7890)) 55 # 设置监听状态 56 tcp_server_socket.listen(128) 57 # 设置非堵塞 58 tcp_server_socket.setblocking(False) 59 60 # 创建一个epoll对象 61 epl = select.epoll() 62 63 # 将监听套接字对应的fd注册到epoll中 64 # select.EPOLLIN监听是否有数据输入 65 epl.register(tcp_server_socket.filno(),select.EPOLLIN) 66 67 # 新建socket存储字典 68 fd_event_dic = dict() 69 70 # 新建为客户端服务的套接字 71 while True: 72 # poll()默认会堵塞 直到os监测到数据到来 通过事件通知方式 告诉这个程序 才会解堵塞 有返回值 返回值是列表 73 fd_event_list = epl.poll() 74 75 #[(fd,event),(套接字对应的文件描述符,这个文件描述符是什么事件 例如 可以调用recv()接收等)] 76 for fd, event in fd_event_list: 77 # 判断fd如果是监听套接字的fd 78 if fd == tcp_server_socket.fileno(): 79 # 创建客户端套接字,接收客户端地址 80 new_client, client_addr = tcp_server_socket.accept() 81 # 将客户端套接字fd注册到epoll中 82 epl.register(new_client.fileno(), select.EPOLLIN) 83 # 将客户端套接字存储到字典 84 fd_event_dic[new_client.fileno()] = new_client 85 # 如果不是监听套接字的fd或者是事件通知 86 elif event == select.EPOLLIN: 87 recv_data = fd_event_dic[fd].recv(1024).decode("utf-8") 88 # 如果有数据 89 if recv_data: 90 # 调用client_server 91 client_server(fd_event_dic[fd], recv_data) 92 # 如果是空数据 93 else: 94 # 调用客户端套接字close() 95 fd_event_dic[fd].close() 96 # 注销在epoll中的客户端套接字fd 97 epl.unregister(fd) 98 # 移出字典 99 del fd_event_dic[fd] 100 101 102 # 关闭套接字 103 tcp_server_socket.close() 104 105 106 if __name__ == '__main__': 107 main()