使用多进程、多线程、协程、非堵塞完成http服务器
多进程:
1 import socket 2 import re 3 import multiprocessing 4 5 6 def service_client(client_socket): 7 """为这个客户端返回数据""" 8 # 1.接收浏览器发送过来的请求 9 request = client_socket.recv(1024).decode('utf-8') 10 headers = request.splitlines() 11 ret = re.match(r'[^/]+(/[^ ]*)', headers[0]) 12 if ret: 13 file_name = ret.group(1) 14 if file_name == '/': 15 file_name = '/index.html' 16 17 # 2.返回http格式的数据给浏览器 18 try: 19 f = open('jQuery响应式网站导航菜单'+file_name, 'rb') 20 except: 21 # 如果浏览器访问的页面不存在,则返回404 22 response = "HTTP/1.1 404 \r\n" 23 response += 'Content-Type: text/html;charset=utf-8\r\n' 24 response += '\r\n' 25 response += "您所访问的地址或文件不存在" 26 client_socket.send(response.encode('utf-8')) 27 else: 28 # 2.1 准备发送给浏览器的header 29 response = "HTTP/1.1 200 OK\r\n" 30 response += 'Content-Type: text/html;charset=utf-8\r\n' 31 response += '\r\n' 32 # 发送网页的headers 33 client_socket.send(response.encode('gbk')) 34 35 # 发送网页的body 36 html_content = f.read() 37 f.close() 38 client_socket.send(html_content) 39 40 # 关闭套接字 41 client_socket.close() 42 43 44 def main(): 45 """用来完成整体的控制""" 46 # 1.创建套接字 47 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 48 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 49 50 # 2.绑定 51 tcp_server_socket.bind(('', 7788)) 52 53 # 3.设置为被动监听 54 tcp_server_socket.listen(128) 55 56 while True: 57 # 4.等待客户端连接 58 client_socket, client_adress = tcp_server_socket.accept() 59 60 # 5.为客户端服务 61 # service_client(client_socket) 62 p = multiprocessing.Process(target=service_client, args=(client_socket,)) 63 p.start() 64 # 创建子进程,会将主进程几乎所有的资源复制一份,因此也会复制客户端套接字的引用。 65 # 当子进程关闭客户端套接字的引用时,主进程也需要关闭套接字 66 client_socket.close() 67 68 # 关闭监听套接字 69 tcp_server_socket.close() 70 71 72 if __name__ == '__main__': 73 main()
多线程(修改main函数):
1 def main(): 2 """用来完成整体的控制""" 3 # 1.创建套接字 4 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 6 7 # 2.绑定 8 tcp_server_socket.bind(('', 7788)) 9 10 # 3.设置为被动监听 11 tcp_server_socket.listen(128) 12 13 while True: 14 # 4.等待客户端连接 15 client_socket, client_adress = tcp_server_socket.accept() 16 17 # 5.为客户端服务 18 # service_client(client_socket) 19 t = threading.Thread(target=service_client, args=(client_socket,)) 20 t.start() 21 22 # 关闭监听套接字 23 tcp_server_socket.close()
协程(修改main函数):
1 from gevent import monkey 2 monkey.patch_all() 3 4 def main(): 5 """用来完成整体的控制""" 6 # 1.创建套接字 7 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 9 10 # 2.绑定 11 tcp_server_socket.bind(('', 7788)) 12 13 # 3.设置为被动监听 14 tcp_server_socket.listen(128) 15 16 while True: 17 # 4.等待客户端连接 18 client_socket, client_adress = tcp_server_socket.accept() 19 20 # 5.为客户端服务 21 # service_client(client_socket) 22 gevent.spawn(service_client, client_socket) 23 24 # 关闭监听套接字 25 tcp_server_socket.close()
非堵塞思想实现单线程单进程并发:
1 import socket 2 3 4 def main(): 5 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 tcp_server_socket.bind(('', 7788)) 7 tcp_server_socket.listen(128) 8 tcp_server_socket.setblocking(False) # 设置监听套接字为非堵塞 9 10 client_socket_list = list() # 设置一个列表储存客户端套接字 11 while True: 12 try: 13 new_socket, client_addr = tcp_server_socket.accept() 14 except: 15 pass 16 # print("没有新的客户端连接") 17 else: 18 print("来了一个客户端") 19 new_socket.setblocking(False) # 设置客户端套接字为非堵塞 20 client_socket_list.append(new_socket) 21 22 # 列表为空是for循环不会执行 23 for client_socket in client_socket_list: 24 try: 25 recv_data = client_socket.recv(1024) 26 except: 27 pass 28 # print("没有接受到客户端的数据") 29 else: 30 if recv_data: 31 print(recv_data.decode('utf-8')) 32 # 当recv有接收但无数据时,证明客户端关闭了连接 33 else: 34 client_socket_list.remove(client_socket) 35 client_socket.close() 36 37 38 if __name__ == '__main__': 39 main()
非堵塞长连接完成返回浏览器需要页面的http服务器:
当需要用长连接时,程序不能主动关闭浏览器套接字,但是不关闭套接字又不能够知道浏览器访问的资源是否已经访问完成,所以需要在response_header中添加Content-Length告诉浏览器response_body有多长,浏览器当访问到这个长度的数据后会获取数据。
代码如下:
1 import socket 2 import re 3 4 5 def service_client(client_socket, requests): 6 """为这个客户端返回数据""" 7 # 1.接收浏览器发送过来的请求 8 # request = client_socket.recv(1024).decode('utf-8') 9 # 将浏览器请求的头部信息,根据换行拆分成一个列表 10 headers = requests.splitlines() 11 # print(headers[0]) # GET /XXX/XXXX HTTP/1.1 12 ret = re.match(r'[^/]+(/[^ ]*)', headers[0]) 13 # print(ret.group()) 14 file_name = '' 15 if ret: 16 # 符合正则的值为GET /XXX/XXX和/XXX/XXX 取后面这个group(1) 17 file_name = ret.group(1) 18 # 如果浏览器请求的是127.0.0.1(也就是只有一个/),那么让浏览器显示主页(index.html) 19 if file_name == '/': 20 file_name = '/index.html' 21 22 # 2.返回http格式的数据给浏览器 23 try: 24 f = open('jQuery响应式网站导航菜单'+file_name, 'rb') 25 except: 26 # 如果浏览器访问的页面不存在,则返回404 27 response = "HTTP/1.1 404 \r\n" 28 response += 'Content-Type: text/html;charset=utf-8\r\n' 29 response += '\r\n' 30 response += "您所访问的地址或文件不存在" 31 # 发送网页的headers 32 client_socket.send(response.encode('utf-8')) 33 else: 34 # 2.1 准备发送给浏览器的header 35 html_content = f.read() 36 f.close() 37 response = "HTTP/1.1 200 OK\r\n" 38 response += 'Content-Type: text/html;charset=utf-8\r\n' 39 response += 'Content-Length: %d\r\n' % len(html_content) 40 response += '\r\n' 41 # 发送网页的headers 42 client_socket.send(response.encode('utf-8')) 43 44 # 发送网页的body 45 client_socket.send(html_content) 46 47 # 关闭套接字 48 # client_socket.close() 49 50 51 def main(): 52 """用来完成整体的控制""" 53 # 1.创建套接字 54 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 56 57 # 2.绑定 58 tcp_server_socket.bind(('', 7788)) 59 60 # 3.设置为被动监听 61 tcp_server_socket.listen(128) 62 tcp_server_socket.setblocking(False) # 将套接字变为非堵塞 63 64 client_socket_list = list() 65 while True: 66 # 4.等待客户端连接 67 try: 68 new_socket, client_adress = tcp_server_socket.accept() 69 except Exception as ret: 70 pass 71 # 5.为客户端服务 72 else: 73 new_socket.setblocking(False) # 将套接字变为非堵塞 74 client_socket_list.append(new_socket) 75 76 for client_socket in client_socket_list: 77 try: 78 recv_data = client_socket.recv(1024).decode('utf-8') 79 except Exception as ret: 80 pass 81 else: 82 if recv_data: 83 service_client(client_socket, recv_data) 84 else: 85 client_socket_list.remove(client_socket) 86 client_socket.close() 87 88 # 关闭监听套接字 89 tcp_server_socket.close() 90 91 92 if __name__ == '__main__': 93 main()