为了了解Django的客户端与服务端的交互原理,我们需要了解Web应用的本质方便以后更加的理解Django原理
在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP。所以本质上就是:
而在http协议中,我们可以用socket来实现
web服务器本质上可以认为是一段代码,可以不断的处理http协议的网络请求,而http协议可以使用socket实现,并且http协议是一个无状态的协议,即浏览器发起请求,服务器接收请求,然后给浏览器回复数据,然后断开连接;那么可以用socket来实现一个最简单的web服务器。
无状态、短连接
不断开(socket连接)
ping 网址 转化为ip地址进行访问(默认监听80端口)
示意如下:
import socket
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1',8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
#等待
conn,addr= sock.accept()
# 有人来连接
# 获取用户发送的数据
data = conn.recv(8096)
# 回数据,转字节
conn.send(b'123123')
# 断开客户端连接
conn.close()
访问127.0.0.1:8080
在html中发送获取数据都遵从着HTTP协议
,随着时间的变化一点一点的在改变,直到现在,我们可以通过socket发来的请求打印data来查看http发送的响应数据:
print(data)
#返回
"""
响应头
状态:200 OK
'GET /(get请求数据默认放这) HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Upgrade-Insecure-Requests: 1'
请求体:如果是post请求数据放这
响应体
'GET /favicon.ico HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: image/webp,*/*' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Referer: http://127.0.0.1:8080/'
"""
很显然我们上面写的代码没有根据HTTP的协议发送请求,应该遵循HTTP协议,用户在页面上看到的内容”字符串“(看到页面效果,是由浏览器解析出来的)
示意如下:
import socket
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1',8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
#等待
conn,addr= sock.accept()
# 有人来连接
#1. 获取用户发送的数据
data = conn.recv(8096)
# 4. 遵循http协议
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 5. 返回数据,转字节
conn.send(b'123123')
# 6. 断开客户端连接
conn.close()
但是我们目前怎么样都只有一个页面,不能像其他的网站一样有多个页面,所以我们要从客户端发来的请求中拿到数据在做出响应在返回:
示意如下:
import socket
# 相应数据函数
def f1(request):
return b"f1"
def f2(request):
return b"f2"
# 类似django的路由
routers = [
('/xxx', f1),
('/ooo', f2),
]
# 主函数
def run():
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1', 8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
# 等待
conn, addr = sock.accept()
# 有人来连接
# 1. 获取用户发送的数据
data = conn.recv(8096)
# 2. 接收请求传来的数据
data = str(data, encoding='utf-8')
# 3. 分割数据,请求头,和请求体
headers, bodys = data.split('\r\n\r\n')
temp_list = headers.split('\r\n')
# 获取一.get请求 二.访问url 三.请求体
res, url, hea = temp_list[0].split(' ')
func_name = None
# 4. 遵循http协议
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 这时候打印的就是后缀的值
print(url)
# 遍历路由
for item in routers:
if item[0] == url:
# 判断是否是该url返回函数地址
func_name = item[1]
break
# 判断函数是否存在
if func_name:
response = func_name(data)
else:
response = b"404 not found"
# 5. 返回相应数据,转字节
conn.send(response)
# 6. 断开客户端连接
conn.close()
if __name__ == '__main__':
run()
访问127.0.0.1:8080
示意如下:
所以通过上面例子我们可以写一个html然后渲染。
因为如果想要数据库实时动态产生,我们需要数据库的帮助,不过在开始介绍前,我们先写静态网页,首先在当前主目录下创建两个文件夹动态网页和静态网页。
用于区分一下动态网页和静态网页。
在静态文件/index.html中定义视图如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title>
head>
<body>
<table border="1">
<thead>
<tr>
<th>IDth>
<th>用户名th>
<th>邮箱th>
tr>
thead>
<tbody>
<tr>
<th>1th>
<th>rootth>
<th>[email protected]th>
tr>
tbody>
table>
body>
html>
在f1中定义函数如下:
def f1(request)
f=open('静态网页/index.html', 'rb')
data=f.read()
f.close()
return data
访问127.0.0.1:8080/xxx
示意如下:
在前面的文章有写到操作数据库的方法,种类很多这里我们使用pymysql来实现,如果没看到这里的可以点开链接:点我!!!进行查看。
pip install pymysql
import pymysql
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='xxx', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 关闭
cursor.close()
conn.close()
在这里我们把/ooo改成/userlist.htm为了告诉大家django的路由和这个很像
routers = [
('/xxx', f1),
('/userlist.htm', f2),
]
在动态网页/userlist.html中定义视图如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title>
head>
<body>
<table border="1">
<thead>
<tr>
<th>IDth>
<th>用户名th>
<th>邮箱th>
tr>
thead>
<tbody>
<tr>
@@content@@
tr>
tbody>
table>
body>
html>
因为返回的是字符串所以我们可以对字符串进行拼接成html看得懂的,然后在从html转化为字节传给浏览器。
在f2中定义函数如下:
def f2(request):
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在数据库查询数据
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 获取所有查到的数据
user_list = cursor.fetchall()
# 关闭
cursor.close()
conn.close()
# 拼接
content_list = []
for row in user_list:
tp = "%s %s %s " % (row['employee_id'], row['first_name'], row['phone_number'])
# 添加进来
content_list.append(tp)
# 拼接到一起
content = "".join(content_list)
# 打开userlist.html
f = open('动态网页/userlist.html', 'r', encoding='utf-8')
# 获得字符串(模板的渲染)
template = f.read()
# 关闭
f.close()
# 把我之前在html写的@@content@@修改
data = template.replace('@@content@@', content)
# 把写好的字符串转成字节的形式给浏览器渲染成可视化界面
return bytes(data, encoding='utf-8')
此时访问127.0.0.1:8080/userlist.htm
对于上面的一系列操作,相对麻烦,又要替换字符串,转字节,还得自己拼接,这时python中就出来了一个模块jinjia2,它可以帮助我们进行模板的渲染,而渲染的规则要和它一致
。
pip install jinja2
routers = [
('/xxx', f1),
('/userlist.htm', f2),
('/host.html',f3),
]
在动态网页/host.html中定义视图如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title>
head>
<body>
<table border="1">
<thead>
<tr>
<th>IDth>
<th>用户名th>
<th>邮箱th>
tr>
thead>
<tbody>
{% for row in user_list %}
<tr>
<td>{
{ row.employee_id }}td>
<td>{
{ row.first_name }}td>
<td>{
{ row.phone_number }}td>
tr>
{% endfor %}
tbody>
table>
body>
html>
在f3中定义函数如下:
from jinja2 import Template
def f3(request):
f = open('动态网页/host.html', 'r', encoding='utf-8')
# 获得字符串(模板的渲染)
data = f.read()
# 关闭
f.close()
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在数据库查询数据
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 获取所有查到的数据
user_list = cursor.fetchall()
# 关闭
cursor.close()
conn.close()
# 使用jinja2渲染
template=Template(data)
data=template.render(user_list=user_list)
# 上传
return data.encode('utf-8')
此时访问此时访问127.0.0.1:8080/host.html
示意如下: