一:Django框架应用
1:web应用:运行在浏览器上的应用
2:c/s,b/s架构
client/server:客户端服务器架构,c++
browser/server:浏览器服务器架构,java,Python
底层均是基于socket
3:Python web框架
a:socket b:页面路由 c:模板渲染
Django a用到wsgiref b自己写的 c 自己写的 功能全面
Flask a用的第三方 b自己写的 c自己写的 小而轻
Tornado a自己写的 b自己写的 c自己写的 支持高并发
二:原生socket服务
目录结构:
part1
-- index.html
-- server.py
基础socket服务:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket # 利用socket建立服务器对象 server = socket.socket() # 设置ip和端口 server.bind(('127.0.0.1', 8001)) # 设置监听 server.listen(5) print('服务器设置成功') print('浏览器访问:http://127.0.0.1:8001') while True: # 阻塞等待客户端数据 client, address = server.accept() # 接收数据 data = client.recv(1024) print('接收到数据: ', data) # 返回数据 client.send(b'Normal Socket Web') # 关闭连接(必须关闭每一次连接) client.close() # 浏览器错误:发送的响应无效,原因:响应不满足http协议 ''' # 请求发来的数据 b'GET / HTTP/1.1 Host: 127.0.0.1:8001 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 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,en;q=0.8 Cookie: csrftoken=szfYLDVuqvRhlveNpNE2rp1GYOcI5x7mRNfvkRWTMRNRwWxXMZWOhL1MqknYJ7jg; sessionid=3pphvmw2icub0bea7nn02u6wev17k4uw ' '''
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>测试请求</title> </head> <body> <div>GET</div> <form action="http://127.0.0.1:8801" method="GET"> <input type="text" name="get_usr"> <input type="submit" value="get提交"> </form> <div>POST</div> <form action="http://127.0.0.1:8801" method="POST"> <input type="text" name="post_usr"> <input type="submit" value="post提交"> </form> </body> </html>
三:HTTP协议
1:什么是HTTP协议
HTTP(HyperText Transport Protocol)是超文本传输协议
基于TCP/IP洗衣的基础上的应用协议,底层实现仍为socket
基于请求--响应模式:通信一定是从客户端开始,服务器接收到客户端一定会做出对应响应
无状态:协议不对任何一次通信状态和任何数据做保存
无连接:一次连接只完成一次请求--响应,请求--响应完毕后会立即断开连接
2.HTTP工作原理(事务)
一次HTTP操作称之为一个事务,工作过程可分为四步:
1:客户端与服务端建立连接
2:客户端发生一个HTTP协议指定格式的请求
3:服务器端接收请求后,响应一个HTTP协议指定格式的响应
4:客户端将服务器的响应显示展现给用户
3.请求报文
请求行:GET|POST(请求方式) / (请求路径) HTTP / 1.1(HTTP协议版本)
请求头:了解
请求体: GET请求体放在请求路径后进行拼接 POST会在请求行与请求头结束后,以数据包方式单独发送
POST / HTTP/1.1 Host: 127.0.0.1:8001 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 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,en;q=0.8 usr=abc&pwd=123
4.响应报文
响应行(一定有):HTTP/1.1(http协议版本) 200(状态码) OK(状态结束)
响应头:(了解)
响应体:具体的数据
HTTP/1.1 200 OK Content-type:text/html Login Success
5.状态码
1打头:消息通知
2打头:请求成功
3打头:重定向
4打头:客户端错误(404)
5打头:服务器端错误
四:练习代码:
part2:目录
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket PORT = 8802 def index(): return b'home page' pass def login(): with open('login.html', 'rb') as f: login_data = f.read() return login_data def ico(): with open('keji.jpg', 'rb') as f: ico_data = f.read() return ico_data method_dic = { '/': index, '/index': index, '/login': login, '/favicon.ico': ico, } server = socket.socket() server.bind(('127.0.0.1', PORT)) server.listen(5) print("服务端启动:http://127.0.0.1:%s" % PORT) # 1.浏览器采用http协议方式发生请求 while True: browser, _ = server.accept() req_data = browser.recv(1024).decode('utf-8') # print(req_data) browser.send(b'HTTP/1.1 200 OK ') browser.send(b'Content-type:text/html ') browser.send(b' ') # 请求路径有多种多样 => 具体请求路径应该交给具体的功能处理完成对应的响应 path = req_data.split(' ')[0].split(" ")[1] print(path) # print() resp_data = b'<h1 style="text-align: center">404</h1>' if path in method_dic: resp_data = method_dic[path]() browser.send(resp_data) browser.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>登录页面</title> </head> <body> <form action=""> <input type="text" name="usr"> <input type="password" name="pwd"> <input type="submit" value="登录"> </form> </body> </html>
可以根据请求HTTP协议数据,解析出请求路径,根据具体路径完成业务逻辑方法,完成对应的响应。
1:业务逻辑会不断增加,所以业务逻辑要分层单独处理
2:路径与业务处理方法对应关系也会越来越多,越来越复杂,也可以分离单独处理
3:自定义服务器功能不健全,稳定性差,可以选择第三方
改善part2写的part3:
目录:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>测试请求</title> </head> <body> <div>GET</div> <form action="http://127.0.0.1:8803" method="GET"> <input type="text" name="get_usr"> <input type="submit" value="get提交"> </form> <div>POST</div> <form action="http://127.0.0.1:8803" method="POST"> <input type="text" name="post_usr"> <input type="submit" value="post提交"> </form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 使用遵循了WSGI协议的第三方server from wsgiref import simple_server from day62.教案.part3.urls import urls PORT = 8803 def send_request(url): resp_data = b'404' if url in urls: resp_data = urls[url]() return resp_data def app(environ, response): # 请求的数据被解析在environ字典中 print(environ) # 请求方式REQUEST_METHOD 请求路径PATH_INFO path = environ['PATH_INFO'] print(path) # 数据的获取 # GET 在environ的QUERY_STRING字段中,eg:usr=abc&pwd=123 # POST 数据长度CONTENT_LENGTH 数据存放的io流 wsgi.input eg: usr=abc&pwd=123 # length = int(environ['CONTENT_LENGTH']) # res = environ['wsgi.input'].read(length) # print(res) # 将数据转换成字典 resp_data = send_request(path) # 用response来规定http响应结果 # print(response) response("200 OK", [('Content-type', 'text/html')]) # 返回的是装有二进制数据的数组 return [resp_data] if __name__ == '__main__': server = simple_server.make_server('127.0.0.1', PORT, app) print("服务启动:http://127.0.0.1:%s" % PORT) # 保持server运行 server.serve_forever() # 利用第三方wsgiref服务器完成请求-响应
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from part3.views import * urls = { '/': index, '/index': index, '/favicon.ico': ico, '/login': login, }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 结果要返回二进制数据 def ico(): with open('keji.jpg', 'rb') as f: ico_data = f.read() return ico_data def index(): return b'home page' pass def login(): with open('login.html', 'rb') as f: login_data = f.read() return login_data
五:jinjia2
Jinjia2是基于Python下一个被广泛应用的模板引擎,来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,最显著的是增加了沙箱执行功能和可选的自动转义功能,者对于大多数的安全性来说是非常重要的。(基于Unicode并能在Python2.4之后的版本运行,包括Python3)
有Jinjia改的part4:
目录:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>login result</title> </head> <body> <!--my login page => %%res%%--> my login page => {{ result }} <hr> {{my_dic}} <hr> {% for k in my_dic %} <div>{{k}} : {{my_dic[k]}}</div> {% endfor %} </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from wsgiref import simple_server from part4.urls import urls PORT = 8804 # 浏览器通过:http://127.0.0.1:8804/login?usr=abc&pwd=123 来模拟get方式登录 def app(environ, response): print(environ) response("200 OK", [('Content-type', 'text/html')]) url = environ['PATH_INFO'] res_dic = {} if environ.get('REQUEST_METHOD', None) == "GET" and url == "/login": res_str = environ['QUERY_STRING'] # usr=abc&pwd=123 if res_str: # 如果没有请求提交的数据,就不需要解析数据 res_list = res_str.split('&') # ['usr=abc', 'pwd=123'] for k_v_m in res_list: # k_v_m: 'usr=abc' | 'pwd=123' k_v_l = k_v_m.split('=') # ['usr', 'abc'] | ['pwd', '123'] res_dic[k_v_l[0]] = k_v_l[1] resp_data = b'404' if url in urls: resp_data = urls[url](res_dic) return [resp_data] if __name__ == '__main__': server = simple_server.make_server('127.0.0.1', PORT, app) print("服务启动:http://127.0.0.1:%s" % PORT) # 保持server运行 server.serve_forever()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from day62.教案.part4.views import * urls = { '/': index, '/index': index, '/favicon.ico': ico, '/login': login, }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 结果要返回二进制数据 import pymysql from jinja2 import Template def ico(dic=None): with open('keji.jpg', 'rb') as f: ico_data = f.read() return ico_data def index(dic=None): return b'home page' pass def login(dic=None): print(dic) usr = dic['usr'] pwd = dic['pwd'] # 和数据库进行交互 conn = pymysql.connect( host='127.0.0.1', db='dg1', user='root', passwd='root' ) cursor = conn.cursor(pymysql.cursors.DictCursor) row = cursor.execute('select * from user where usr=%s and pwd = %s', [usr, pwd]) # 根据结果渲染动态页面 => 利用第三方jinja2 # with open('login.html', 'rt') as f: # msg = f.read() # # if row: # res = 'login success' # else: # res = 'login failed' # # msg = msg.replace("%%res%%", res) # 模板渲染框架定义了很多模板语法,采用这些语法可以复用html代码 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, my_dic=dic) return msg.encode('utf-8')