一.导读
1.web应用:运行在浏览器上的应用。
2.c/s b/s 架构:
client/server:客户端/服务器架构,C++
brower/server:浏览器/服务器架构,Java,Python
3.python web框架:
Django:socket用的是wsgiref,页面路由用的自己写的,模板渲染用的自己写的,特点:功能全面。
Flask:socket用的是第三方,页面路由用的自己写的,模板渲染用的自己写的,特点:小而轻。
Tornado:socket用的是自己写的,页面路由用的自己写的,模板渲染用的自己写的,特点:支持高并发。
二.HTTP协议
1.什么是http协议?
HTTP(HyperText Transport Protocol)指的是超文本传输协议,它是基于TCP/IP协议基础上的应用层协议,底层实现还是socket,它是基于请求-响应模式:通信一定是从客户端开始,服务器端接收到客户端一定会做出对应响应。
无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接。
无状态:协议不对任何一次通信状态和任何数据进行保存。
2.http工作原理(事务)
一次http操作称之为一个事务,工作过程可以分为四步:
1.客户端与服务器建立连接;
2.客户端发生一个http协议指定格式的请求;
3.服务器端接收请求后,响应一个http协议指定格式的响应;
4.客户端将服务器的响应显示展现给用户。
import socket PORT = 9999 server = socket.socket() server.bind(('127.0.0.1', PORT)) server.listen(5) print("服务器启动:http://127.0.0.1:%s" % PORT) while True: brower, addr = server.accept() req_data = brower.recv(1024).decode('utf-8') # print(req_data) # 响应报文 # 遵循http协议返回数据:响应行 响应头 响应体 brower.send(b'HTTP/1.1 200 OK ') # 响应行(必须),以 结束 brower.send(b'Content-type:text/html ') # 响应头(可选),服务于响应头 brower.send(b' ') # 响应规则与响应体内容之间还需要一个 进行分割 brower.send(b'hahaha') # 响应体(可选) brower.send(b'<h1>hello world</h1>') brower.close() # 必须关闭每一次的连接 # 请求报文 ''' GET / HTTP/1.1 Host: 127.0.0.1:9999 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 GET /favicon.ico HTTP/1.1 # /favicon.ico指的是页面头的图标 Host: 127.0.0.1:9999 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36 Accept: image/webp,image/apng,image/*,*/*;q=0.8 Referer: http://127.0.0.1:9999/ Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 '''
3.http处理响应
import socket PORT = 9999 server = socket.socket() server.bind(('127.0.0.1', PORT)) server.listen(5) print("服务器启动:http://127.0.0.1:%s" % PORT) def index(): pass def login(): pass def ico(): with open('1.jpg', 'rb') as f: data = f.read() return data func_dic = { '/': index, '/index': index, '/login': login, '/favicon.ico': ico } while True: brower, addr = server.accept() req_data = brower.recv(1024).decode('utf-8') # print(req_data) # 响应报文 # 遵循http协议返回数据:响应行 响应头 响应体 brower.send(b'HTTP/1.1 200 OK ') # 响应行(必须),以 结束 brower.send(b'Content-type:text/html ') # 响应头(可选),服务于响应头 brower.send(b' ') # 响应规则与响应体内容之间还需要一个 进行分割 # GET / HTTP/1.1 # GET /favicon.ico HTTP/1.1 path = req_data.split(' ')[0].split(' ')[1] # 请求路径 # 根据路径,处理不同的请求方法 if path in func_dic: res_dic = func_dic[path]() res_dic = b'<h1 style="text-align: center">404</h>' brower.send(res_dic) # 响应体(可选) brower.close() # 必须关闭每一次的连接
基于上面的基础b/s架构,可以根据请求http协议数据,解析出请求路径,根据具体路径完成业务逻辑,的方法,完成对应的响应。
1.由于业务逻辑会不断的增加,所以业务逻辑要进行分层单独处理;
2.路径与业务处理方法对应关系也会越来越多,越来越复杂,也可以分离单独处理;
3.自定义服务器功能不健全,稳定性差,可以选择第三方
server.py import socket from wsgi协议.urls import url_dic PORT = 9999 server = socket.socket() server.bind(('127.0.0.1', PORT)) server.listen(5) print("服务器启动:http://127.0.0.1:%s" % PORT) while True: brower, addr = server.accept() req_data = brower.recv(1024).decode('utf-8') # print(req_data) # 响应报文 # 遵循http协议返回数据:响应行 响应头 响应体 brower.send(b'HTTP/1.1 200 OK ') # 响应行(必须),以 结束 brower.send(b'Content-type:text/html ') # 响应头(可选),服务于响应头 brower.send(b' ') # 响应规则与响应体内容之间还需要一个 进行分割 # GET / HTTP/1.1 # GET /favicon.ico HTTP/1.1 url = req_data.split(' ')[0].split(' ')[1] # 请求路径 # 根据路径,处理不同的请求方法 if url in url_dic: res_dic = url_dic[url]() res_dic = b'<h1 style="text-align: center">404</h>' # 响应错误时返回404 brower.send(res_dic) # 响应体(可选) brower.close() # 必须关闭每一次的连接 urls.py from wsgi协议.views import * url_dic = { '/': index, '/index': index, '/login': login, '/favicon.ico': ico } views.py def index(): pass def login(): pass def ico(): with open('1.jpg', 'rb') as f: data = f.read() return data
4.状态码
# 1打头:消息通知
# 2打头:请求成功
# 3打头:重定向
# 4打头:客户端错误
# 5打头:服务器端错误
三.WSGI协议
利用第三方wsgiref服务器完成http请求-响应:
通过wsgiref模块创建server时,有三个参数分别是:Host,Port,app,其中app时一个回调函数,该回调函数有两个参数:environ和response;environ是一个字典,请求的数据都被解析在该字典中,用response来规定http响应的结果。
server.py from wsgiref import simple_server from wsgi协议.urls import url_dic PORT = 8888 # 回调函数 def app(environ, response): # 请求的数据都被解析在environ字典中 # 请求方式的key:REQUEST_METHOD # 'REQUEST_METHOD': 'GET' # 请求的路径的key:PATH_INFO # 'PATH_INFO': '/' print(environ) # 用response来规定http响应的结果 response('200 OK', [('Content-type', 'text/html')]) # 数据的获取 # 解析给key,得到数据 method = environ['REQUEST_METHOD'] # 请求方法 res_dic = {} url = '' if method == 'GET': url = environ['PATH_INFO'] # 请求路径 # 在GET方法中:数据在environ中的QUERY_STRING对应的value res_str = environ['QUERY_STRING'] if res_str: res_list = res_str.split('&') for i in res_list: data_list = i.split('=') res_dic[data_list[0]] = data_list[1] elif method == 'POST': # 在POST方法中:数据存放的io流'wsgi.input': <_io.BufferedReader name=552>;数据长度'CONTENT_LENGTH': '40' length = int(environ['CONTENT_LENGTH']) if length == 0: return res_str = environ['wsgi.input'].read(length).decode('utf-8') res_list = res_str.split('&') for i in res_list: data_list = i.split('=') if i is res_list[-1]: url = '/' + data_list[0] # print(url) continue res_dic[data_list[0]] = data_list[1] # print(res_dic) req_dic = b'<h1 style="text-align: center">404</h1>' if url in url_dic: req_dic = url_dic[url](res_dic) # print(res_dic) # 返回的是装有二进制数据的列表 return [req_dic] if __name__ == '__main__': server = simple_server.make_server('127.0.0.1', PORT, app=app) # 通过wsgiref创建server print('服务器启动:http://127.0.0.1:%s' % PORT) # 保持server一直运行 server.serve_forever() views.py import pymysql from jinja2 import Template def execute(sql, args): conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='root', db='db1', charset='utf8', autocommit=True ) cursor = conn.cursor(pymysql.cursors.DictCursor) affect_row = cursor.execute(sql, args=args) return affect_row def index(dic): pass def login(dic): # print(dic) name = dic['usr'] pwd = dic['pwd'] sql = "select * from user where name=%s and pwd=%s" args = [name, pwd] row = execute(sql, args) with open('login.html', 'rt') as f: msg = f.read() tem = Template(msg) if row: res = 'login success' else: res = 'login failed' msg = tem.render(result=res) return msg.encode('utf-8') def register(dic): pass def ico(dic): with open('timg.jpg', 'rb') as f: data = f.read() return data login.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>title</title> </head> <body> {{ result }} </body> </html> urls.py from wsgi协议.views import * url_dic = { '/': index, '/index': index, '/login': login, '/favicon.ico': ico } deom.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="http://127.0.0.1:8888" method="post"> <input type="text" name="usr"> <br> <input type="password" name="pwd"> <br> <input type="submit" value="login" name="login"> </form> </body> </html>