python3 简单web服务器
补充:tcp长连接和短连接
长连接:
客户端向服务器发起连接请求,服务器接收到请求回应给客户端,双方完成三次握手,然后客户端发送消息,服务端回应消息,每一次完成读写操作,套接字不不关闭,也就是连接不关闭,继续保持连接,等待下一次的读写操作,长时间之后客户端发起关闭请求。
短连接:
短连接则是一般只会在 client/server 间传递一次读写操作,一次读写操作之后就关闭连接,下一次的操通信则又从三次握手开始重新建立连接。
简单的http服务器:
__author__ = 'Administrator' import socket def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024) # 2.返回浏览器http格式的数据 # 2.1准备发送给浏览器的数据——header # \r\n给Windows使用 response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 2.2准备发送给浏览器的数据——body response+="<h1>哈哈哈哈哈<h2>" new_socket.send(response.encode("utf-8")) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) # 4.等待新客户端的链接 while True: new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 service_client(new_socket) # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
返回给浏览器相应的页面:
Python splitlines() 按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。
str.splitlines([keepends])
keepends -- 在输出结果里是否保留换行符('\r', '\r\n', \n'),默认为 False,不包含换行符,如果为 True,则保留换行符。
__author__ = 'Administrator' import socket def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024) print("===="*100) print(request) # 2.返回浏览器http格式的数据 # 2.1准备发送给浏览器的数据——header # \r\n给Windows使用 response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 2.2准备发送给浏览器的数据——body f=open("./html/index.html","rb") html_content=f.read() f.close() # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response body发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) # 4.等待新客户端的链接 while True: new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 service_client(new_socket) # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
根据用户需求返回相应的页面(对页面进行操作之后返回的页面):
__author__ = 'Administrator' import socket import re def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024).decode("utf-8") # print("===="*100) # print(request) request_lines = request.splitlines() print("") print(">>>>"*20) print(request_lines) # GET /index.html HTTP/1.1 file_name="" res=re.match(r"[^/]+(/[^ ]*)",request_lines[0]) if res: file_name=res.group(1) if file_name=="/": file_name="/index.html" # 2.返回浏览器http格式的数据 # \r\n给Windows使用 try: # 忘了,复习一下 # try中是尝试执行的代码 # except 中是对错误进行处理 # else中是没有异常才会执行的代码 f=open("./html"+file_name,"rb") except: response="HTTP/1.1 404 NOT FOUND\r\n" response+="\r\n" response+="————file not found————" new_socket.send(response.encode("utf-8")) else: html_content=f.read() f.close() response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response body发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) # 4.等待新客户端的链接 while True: new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 service_client(new_socket) # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
使用多进程完成http服务器:
__author__ = 'Administrator' import socket import re import multiprocessing def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024).decode("utf-8") # print("===="*100) # print(request) request_lines = request.splitlines() print("") print(">>>>"*20) print(request_lines) # GET /index.html HTTP/1.1 file_name="" res=re.match(r"[^/]+(/[^ ]*)",request_lines[0]) if res: file_name=res.group(1) if file_name=="/": file_name="/index.html" # 2.返回浏览器http格式的数据 # \r\n给Windows使用 try: # 忘了,复习一下 # try中是尝试执行的代码 # except 中是对错误进行处理 # else中是没有异常才会执行的代码 f=open("./html"+file_name,"rb") except: response="HTTP/1.1 404 NOT FOUND\r\n" response+="\r\n" response+="————file not found————" new_socket.send(response.encode("utf-8")) else: html_content=f.read() f.close() response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response body发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) while True: # 4.等待新客户端的链接[这里类似银行的柜台窗口] new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 p=multiprocessing.Process(target=service_client,args=(new_socket,)) p.start() # 在主进程创建子进程之后,子进程会复制主进程的所有资源,所有就会有两个相同的new_socket套接字 #在service_client()中是子进程关闭了套接字,那在主进程中也是需要关闭该套接字 new_socket.close() # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
使用多线程完成http服务器:
__author__ = 'Administrator' import socket import re import threading def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024).decode("utf-8") # print("===="*100) # print(request) request_lines = request.splitlines() print("") print(">>>>"*20) print(request_lines) # GET /index.html HTTP/1.1 file_name="" res=re.match(r"[^/]+(/[^ ]*)",request_lines[0]) if res: file_name=res.group(1) if file_name=="/": file_name="/index.html" # 2.返回浏览器http格式的数据 # \r\n给Windows使用 try: # 忘了,复习一下 # try中是尝试执行的代码 # except 中是对错误进行处理 # else中是没有异常才会执行的代码 f=open("./html"+file_name,"rb") except: response="HTTP/1.1 404 NOT FOUND\r\n" response+="\r\n" response+="————file not found————" new_socket.send(response.encode("utf-8")) else: html_content=f.read() f.close() response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response body发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) while True: # 4.等待新客户端的链接 new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 p=threading.Thread(target=service_client,args=(new_socket,)) p.start() # 注意:这里和进程的区别,多线程之间共享变量,子线程没有复制主线程 # new_socket.close() # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
使用gevent(协程)完成http服务器:
__author__ = 'Administrator' import socket import re import gevent from gevent import monkey monkey.patch_all() def service_client(new_socket): # 1.接收浏览器发送过来的请求,即http请求 request=new_socket.recv(1024).decode("utf-8") # print("===="*100) # print(request) request_lines = request.splitlines() print("") print(">>>>"*20) print(request_lines) # GET /index.html HTTP/1.1 file_name="" res=re.match(r"[^/]+(/[^ ]*)",request_lines[0]) if res: file_name=res.group(1) if file_name=="/": file_name="/index.html" # 2.返回浏览器http格式的数据 # \r\n给Windows使用 try: # 忘了,复习一下 # try中是尝试执行的代码 # except 中是对错误进行处理 # else中是没有异常才会执行的代码 f=open("./html"+file_name,"rb") except: response="HTTP/1.1 404 NOT FOUND\r\n" response+="\r\n" response+="————file not found————" new_socket.send(response.encode("utf-8")) else: html_content=f.read() f.close() response="HTTP/1.1 200 OK\r\n" response+="\r\n" # 将response header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response body发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) while True: # 4.等待新客户端的链接 new_socket,client_addr=tcp_server.accept() # 5.为这个客户端服务 gevent.spawn(service_client,new_socket) # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
单进程、线程、非堵塞实现并发:
__author__ = 'Administrator' import socket tcp_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcp_socket.bind(("",8081)) tcp_socket.listen(128) # 设置套接字为非堵塞方式 tcp_socket.setblocking(False) client_list=list() # 在没有设置套接字为堵塞的时候,accept和recv默认都是堵塞的,那客户端没有发送链接或者没有发送数据过来都是不会报异常的, # 现在设置套接字为非堵塞,那么此时accept就是非堵塞的,那客户端没有发送链接或者没有发送数据过来都是报异常的, while True: try: new_tcp_socket,addr=tcp_socket.accept() except Exception as res: print(res) print("————没有新的客户端链接————") else: # 设置新的套接字为非堵塞,默认堵塞的时候,不发数据过来不会异常,但是此时非堵塞不发数据过来就会异常 new_tcp_socket.setblocking(False) client_list.append(new_tcp_socket) for client_socket in client_list: try: recv_data=new_tcp_socket.recv(1024) except Exception as res: print(res) print("————客户端没有发送数据过来————") else: print(recv_data) if recv_data: print("————客户端发送数据过来————") else: client_list.remove(new_tcp_socket) new_tcp_socket.close() print("————客户端已经关闭了————")
单进程、线程、非堵塞、长连接实现http服务器:
__author__ = 'Administrator' import socket import re def service_client(new_socket,request): # 1.接收浏览器发送过来的请求,即http请求 # request=new_socket.recv(1024).decode("utf-8") # print("===="*100) # print(request) request_lines = request.splitlines() print("") print(">>>>"*20) print(request_lines) # GET /index.html HTTP/1.1 file_name="" res=re.match(r"[^/]+(/[^ ]*)",request_lines[0]) if res: file_name=res.group(1) if file_name=="/": file_name="/index.html" # 2.返回浏览器http格式的数据 # \r\n给Windows使用 try: # 忘了,复习一下 # try中是尝试执行的代码 # except 中是对错误进行处理 # else中是没有异常才会执行的代码 f=open("./html"+file_name,"rb") except: response="HTTP/1.1 404 NOT FOUND\r\n" response+="\r\n" response+="————file not found————" new_socket.send(response.encode("utf-8")) else: html_content=f.read() f.close() response_body=html_content 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 # 将response 发送给浏览器 new_socket.send(response) # 3.关闭套接字 # 在这个地方服务器强制关闭了套接字,表明了并不是长连接,而是短连接 # new_socket.close() def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.绑定 tcp_server.bind(("",8081)) # 3.监听套接字 tcp_server.listen(128) tcp_server.setblocking(False) # 将套接字设置为非堵塞 client_list=list() # 4.等待新客户端的链接 while True: try: new_socket,client_addr=tcp_server.accept() except Exception as res: pass else: new_socket.setblocking(False) client_list.append(new_socket) for client_socket in client_list: try: recv_data = client_socket.recv(1024).decode("utf-8") except Exception as res: pass else: if recv_data: service_client(client_socket,recv_data) else: client_socket.close() client_list.remove(client_socket) # 6.关闭监听套接字 tcp_server.close() if __name__ == '__main__': main()
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发链接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
通信过程:
浏览器访问服务器的过程
在浏览器访问http:www.baidu.com(域名),该电脑先检查是否认识默认网关的MAC地址,如果没有以arp广播,
广播过去该电脑的默认网关地址组织一个域名解析的数据请求发送给网关,网关发送到互联网上,互联网经过一层一层的转发,
到达DNS服务器,其把域名解析出来之后把ip地址回送给浏览器,然后把ip发送给网关,网关把数据发送到互联网,把数据转发给服务器,
发送到服务器,客户端和浏览器三次握手成功,浏览器把请求发送给服务器,服务器解析请求返回数据给客户端,
浏览器显示,显示成功之后,然后四次挥手,结束。

浙公网安备 33010602011771号