一.web应用框架

  Web应用框架(Web application framework)是一种计算机软件框架,用来支持动态网站、网络应用程序及网络服务的开发。这种框架有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准模板以及会话管理等,可提升代码的可再用性。

  我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。

二.自定义web框架原理

  框架的原理代码:

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen()
conn,addr = sk.accept()
from_b_msg = conn.recv(1024)
str_msg = from_b_msg.decode('utf-8')
#socket是应用层和传输层之间的抽象层,每次都有协议,协议就是消息格式,那么传输层的消息格式我们不用管,因为socket帮我们搞定了,但是应用层的协议还是需要咱们自己遵守的,所以再给浏览器发送消息的时候,如果没有按照应用层的消息格式来写,那么你返回给浏览器的信息,浏览器是没法识别的。而应用层的协议就是我们的HTTP协议,所以我们按照HTTP协议规定的消息格式来给浏览器返回消息就没有问题了conn.send(b'HTTP/1.1 200 ok \r\n\r\nhello')的效果
#下面这句就是按照http协议来写的
# conn.send(b'HTTP/1.1 200 ok \r\n\r\nhello')
#上面这句还可以分成下面两句来写
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
conn.send(b'hello')

1.在浏览器中输入本地地址,打开页面

可以在浏览器按下f12选择network选项查看发送的请求

2. 可以看到对服务器有三个请求

下面看一下http的GET请求内容(就是上面代码中的str_msg), 这个内容是byte型的需要在py文件中decode解码成以下格式:

这些就是得到的结果,第一行就是浏览器请求的文件. 这些内容是字符串,所以我们可以进行处理,通过.split()方法处理之后可以拿得到请求路径.之后浏览器进行请求的时候,可以根据请求的文件路径来决定服务器发送的内容(网页文件).请求的路径显示如下图:

现在我们来自定义一个简单的框架,用来熟悉原理,准备的文件结构如下图

  文件代码如下:

    1.ico(图标文件)和timg.jpg可以自己任意准备一张

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>主页</h1>
</body>
</html>

home.html

h1{
color: blueviolet;
} img{
height: 500px;
width: 400px;
}

index.css

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="1.ico">
<link rel="stylesheet" href="index.css">
</head>
<body>
<h1>嘻嘻嘻</h1>
<img src="timg.jpg" alt="xxx">
</body>
</html>

index.html

import socket
from threading import Thread def ico(conn):
with open('1.ico', 'rb') as f:
ico = f.read()
conn.send(ico)
conn.close() def img(conn):
with open('timg.jpg', 'rb') as f:
img = f.read()
conn.send(img)
conn.close() def css(conn):
with open('index.css', 'rb') as f:
img = f.read()
conn.send(img)
conn.close() def index(conn):
with open('index.html', 'rb') as f:
data = f.read()
conn.send(data)
conn.close() server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen() while 1:
conn,addr = server.accept() msg = conn.recv(1024).decode('utf-8')
path = msg.split("\n")[0].split()[1]
print(path)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n') lst = [('/',index),('/timg.jpg',img),('/index.css',index),('/1.ico',ico)]
for el in lst:
if path == el[0]:
t = Thread(target=el[1],args=(conn,))
t.start()

简单框架演示.py

三.自定义框架的不同实现

1.基本实现

import socket

server = socket.socket()
server.bind(('127.0.0.1',8001))
server.listen()
conn,addr = server.accept()
from_b_msg = conn.recv(1024)
str_msg = from_b_msg.decode('utf-8')
print('浏览器请求信息:',str_msg) # conn.send(b'HTTP/1.1 200 ok \r\ncontent-type:text/html;charset=utf-8;\r\n')
conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open(index.html','rb') as f:
f_data = f.read()
conn.send(f_data)

2.浏览器发送的请求不同,我们响应的数据也不同,所以要根据不同的请求路径,响应不同的文件内容

import socket

sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen() #首先浏览器相当于给我们发送了多个请求,一个是请求我们的html文件,而我们的html文件里面的引入文件的标签又给我们这个网站发送了请求静态文件的请求,所以我们要将建立连接的过程循环起来,才能接受多个请求
while 1:
conn,addr = sk.accept()
# while 1:
from_b_msg = conn.recv(1024)
str_msg = from_b_msg.decode('utf-8')
#通过http协议我们知道,浏览器请求的时候,有一个请求内容的路径,通过对请求信息的分析,这个路径我们在请求的所有请求信息中可以提炼出来,下面的path就是我们提炼出来的路径
path = str_msg.split('\r\n')[0].split(' ')[1]
print('path>>>',path)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
#由于整个页面需要html、css、js、图片等一系列的文件,所以我们都需要给人家浏览器发送过去,浏览器才能有这些文件,才能很好的渲染你的页面
#根据不同的路径来返回响应的内容
if path == '/': #返回html文件
print(from_b_msg)
with open('index','rb') as f:
# with open('Python开发.html','rb') as f:
data = f.read()
conn.send(data)
conn.close()
elif path == '/timg.jpg': #返回图片
with open('meinv.png','rb') as f:
pic_data = f.read()
# conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
conn.send(pic_data)
conn.close()
elif path == '/index.css': #返回css文件
with open('test.css','rb') as f:
css_data = f.read()
conn.send(css_data)
conn.close() elif path == '/1.ico':#返回页面的ico图标
with open('wechat.ico','rb') as f:
ico_data = f.read()
conn.send(ico_data)
conn.close() #注意:上面每一个请求处理完之后,都有一个conn.close()是因为,HTTP协议是短链接的,一次请求对应一次响应,这个请求就结束了,所以我们需要写上close,不然浏览器自己断了,你自己写的服务端没有断,就会出问题。

3.过多的if-elif 显得特别冗杂,我们可以将选项放入列表中做一个判断,再将功能封装进函数,将路径和函数对应起来

sk = socket.socket()
sk.bind(('127.0.0.1',8001))
sk.listen() #处理页面请求的函数
def func1(conn):
with open('index.html', 'rb') as f:
# with open('index.html','rb') as f:
data = f.read()
conn.send(data)
conn.close() #处理页面img标签src属性值是本地路径的时候的请求
def func2(conn):
with open('timg.jpg', 'rb') as f:
pic_data = f.read()
# conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
conn.send(pic_data)
conn.close()
#处理页面link( <link rel="stylesheet" href="test.css">)标签href属性值是本地路径的时候的请求
def func3(conn):
with open('index.css', 'rb') as f:
css_data = f.read()
conn.send(css_data)
conn.close() while 1:
conn,addr = sk.accept()
# while 1:
from_b_msg = conn.recv(1024)
str_msg = from_b_msg.decode('utf-8')
path = str_msg.split('\r\n')[0].split(' ')[1]
print('path>>>',path)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
print(from_b_msg)
if path == '/':
func1(conn)
elif path == '/meinv.png':
func2(conn)
elif path == '/test.css':
func3(conn)

4.可以用多线程处理,增加并发能力

import socket
from threading import Thread def ico(conn):
with open('1.ico', 'rb') as f:
ico = f.read()
conn.send(ico)
conn.close() def img(conn):
with open('timg.jpg', 'rb') as f:
img = f.read()
conn.send(img)
conn.close() def css(conn):
with open('index.css', 'rb') as f:
img = f.read()
conn.send(img)
conn.close() def index(conn):
with open('index.html', 'rb') as f:
data = f.read()
conn.send(data)
conn.close() server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen() while 1:
conn,addr = server.accept() msg = conn.recv(1024).decode('utf-8')
path = msg.split("\n")[0].split()[1]
print(path)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n') lst = [('/',index),('/timg.jpg',img),('/index.css',index),('/1.ico',ico)]
for el in lst:
if path == el[0]:
t = Thread(target=el[1],args=(conn,))
t.start()

5.根据不同路径返回不同页面的web框架

  还可以根据用户访问的不同路径,返回不同的页面,比如访问127.0.0.1:8080/home 和 127.0.0.1:8080/index 两种不同的路径.

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
sk.listen() # 监听 # 将返回不同的内容部分封装成函数
def index(url):
# 读取index.html页面的内容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字节数据
return bytes(s, encoding="utf8") def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系
list1 = [
("/index/", index),
("/home/", home),
] while 1:
# 等待连接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客户端发来的消息
# 从data中取到路径
data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同内容
func = None # 定义一个保存将要执行的函数名的变量
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!" # 返回具体的响应消息
conn.send(response)
conn.close()

6.返回动态页面

  这个动态页面不是有动态效果的页面,意思是里面有动态变化的数据.

import socket
from threading import Thread
import datetime def index(conn):
with open('web框架/index.html', 'rb') as f:
index = f.read()
t = datetime.datetime.now()
snd_msg = index.decode('utf-8').replace('嘻嘻嘻',str(t))
conn.send(snd_msg.encode('utf-8')) def home(conn):
with open('web框架/home.html', 'rb') as f:
home = f.read()
conn.send(home) server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen() while 1:
conn,addr = server.accept() msg = conn.recv(1024).decode('utf-8')
path = msg.split("\n")[0].split()[1]
print(path)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n') # if path == '/home':
# home(conn) lst1 = [('/',index),('/home',home)]
fn = ''
for el in lst1:
print(el)
if el[0] == path:
fn = el[1]
t = Thread(target=fn,args=(conn,))
t.start()

四.wsgiref模块实现的简单web框架

  wsgiref模块其实就是将整个请求信息给封装了起来,就不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象,那么你直接request.path就能获取到用户这次请求的路径,request.method就能获取到本次用户请求的请求方式(get还是post)等

WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

  常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

from wsgiref.simple_server import make_server
# wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便)
#函数名字随便起
def application(environ, start_response):
'''
:param environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息
:param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数
:return:
'''
start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')])
print(environ)
print(environ['PATH_INFO']) #输入地址127.0.0.1:8000,这个打印的是'/',输入的是127.0.0.1:8000/index,打印结果是'/index'
return [b'<h1>Hello, web!</h1>'] #和咱们学的socketserver那个模块很像啊
httpd = make_server('127.0.0.1', 8080, application) print('Serving HTTP on port 8080...')
# 开始监听HTTP请求:
httpd.serve_forever()

  

  

django系列1--介绍与简单原理, wsgiref模块的更多相关文章

  1. django的简单原理

    一.自定义客户端和服务端的请求响应 1.客户端打开url,向服务器发出请求 2.服务端用socket写一个py,用于接收请求和做出响应 3.服务端接收请求 4.服务端模拟HTTP协议做出响应,状态行为 ...

  2. django的下载安装,目录结构的介绍,简单的django应用

    1.django的下载安装 pip3 install django==1.11.9 2.django的创建 1.在windows的cmd窗口下 1.1创建django项目 django-admin s ...

  3. Django 系列博客(一)

    Django 系列博客(一) 前言 学习了 python 这么久,终于到了Django 框架.这可以说是 python 名气最大的web 框架了,那么从今天开始会开始从 Django框架的安装到使用一 ...

  4. Django项目结构介绍

    官网下载网址:https://www.djangoproject.com/download/ 安装(安装最新LTS版): pip3 install django==2.0.7 创建一个django项目 ...

  5. Django 系列博客(十)

    Django 系列博客(十) 前言 本篇博客介绍在 Django 中如何对数据库进行增删查改,主要为对单表进行操作. ORM简介 查询数据层次图解:如果操作 mysql,ORM 是在 pymysql ...

  6. Django 系列博客(九)

    Django 系列博客(九) 前言 本篇博客介绍 Django 模板的导入与继承以及导入导入静态文件的几种方式. 模板导入 模板导入 语法:``{% include '模板名称' %} 如下: < ...

  7. Django 系列博客(八)

    Django 系列博客(八) 前言 本篇博客介绍 Django 中的模板层,模板都是Django 使用相关函数渲染后传输给前端在显式的,为了想要渲染出我们想要的数据,需要学习模板语法,相关过滤器.标签 ...

  8. Django 系列博客(七)

    Django 系列博客(七) 前言 本篇博客介绍 Django 中的视图层中的相关参数,HttpRequest 对象.HttpResponse 对象.JsonResponse,以及视图层的两种响应方式 ...

  9. Django 系列博客(六)

    Django 系列博客(六) 前言 本篇博客介绍 Django 中的路由控制部分,一个网络请求首先到达的就是路由这部分,经过路由与视图层的映射关系再执行相应的代码逻辑并将结果返回给客户端. Djang ...

随机推荐

  1. windows下使用nginx配置tomcat集群

    转自:https://blog.csdn.net/csdn15698845876/article/details/80658599

  2. Vue cli 脚手架使用

    1:基本的安装 安装node 安装npm Windows 更改环境变量 重启 环境变量生效 安装vue-cli 安装webpack 2:项目构建 https://segmentfault.com/a/ ...

  3. Maven(七) maven 常用命令

    转载于:http://blog.csdn.net/hynet/article/details/8664747 1. 用Maven 命令创建一个简单的 Maven 项目 在cmd中运行如下命令: mvn ...

  4. idea 插件

    https://plugins.jetbrains.com/plugin/4509-statistic

  5. FD_CLOEXEC

    [FD_CLOEXEC] 通过fcntl设置FD_CLOEXEC标志有什么用? close on exec, 意为如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不 ...

  6. Android给ListView添加一个入场动画

    动画是一个App体现良好交互的一种手段,通常的我们会看到很多App的ListView的Item都有一个入场动画例如: 可以看到,当进入界面加载ListView的Item的时候有一个向左滑动显示,并且淡 ...

  7. springmvc web.xml配置之 -- SpringMVC IOC容器初始化

    SpringMVC IOC容器初始化 首先强调一下SpringMVC IOC容器初始化有些特别,在SpringMVC中除了生成一个全局的spring Ioc容器外,还会为DispatcherServl ...

  8. SpringAop及拦截器

    一.Aop Aop,面向切面编程,提供了一种机制,在执行业务前后执行另外的代码. 切面编程包括切面(Aspect),连接点(Joinpoint).通知(Advice).切入点(Pointcut).引入 ...

  9. sqlconnection dispose()与close()的区别

    区别: IDispose接口可以通过Using关键字实现使用后立刻销毁,因此,Dispose适合只在方法中调用一次SqlConnection对象,而Close更适合SqlConnection在关闭后可 ...

  10. [leetcode]621. Task Scheduler任务调度

    Given a char array representing tasks CPU need to do. It contains capital letters A to Z where diffe ...