@


我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客服端。

这样我们就可以自己实现Web框架了:

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() while True:
conn, addr = sk.accept()
data = conn.recv(9000)
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行
conn.send(b'Hello,world!')
conn.close()

可以说Web服务本质上都是在这十几行代码基础上扩展出来的,这段代码就是它们的祖宗。

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?

所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。

HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?

让我们首先打印下我们在服务端接收到的消息是什么:

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() while True:
conn, addr = sk.accept()
data = conn.recv(9000)
print(data) # 将浏览器发来的消息打印出来
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
conn.send(b'Hello,world!')
conn.close()

输出如下:

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n'

然后我们再看一个我们访问博客官网时浏览器收到的响应信息是什么,响应的相关信息可以在浏览器调试窗口的network标签页中看到:



我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了。

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。HTTP响应的Header中有一个Content-Type表明响应的内容格式。如text/html表示HTML网页.

HTTP GET请求的格式:

HTTP响应的格式:


根据不同的路径返回不同的内容

普通版

思路:从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断.

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字节类型的数据转换成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行 if url == '/index':
response = 'index'
elif url == '/home':
response = 'home'
else:
response = '404 not found!' conn.send(bytes(response, encoding='UTF-8'))
conn.close()

上面的代码解决了对于不同URL路径返回不同内容的需求。

但是问题又来了,如果有很多的路径要判断怎么办呢?难道要挨个写if判断?

答案是否定的,我们有更聪明的办法:

函数版

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() # 将返回不同的内容部分封装成函数
def index(url):
s = "这是%s页面!" % url
return bytes(s, encoding='UTF-8') def home(url):
s = "这是%s页面!" % url
return bytes(s, encoding='UTF-8') while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字节类型的数据转换成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行 if url == '/index':
response = index(url)
elif url == '/home':
response = home(url)
else:
response = b'404 not found!' conn.send(response)
conn.close()

看起来上面的代码还是要挨个写if判断,怎么办?

只要思想不滑坡,方法总比问题多,走着!

函数进阶版

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() # 将返回不同的内容部分封装成函数
def index(url):
s = "这是%s页面!" % url
return bytes(s, encoding='UTF-8') def home(url):
s = "这是%s页面!" % url
return bytes(s, encoding='UTF-8') # 定义一个url和实际要执行的函数的对应关系
url_list = [
('/index', index),
('/home', home)
] while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字节类型的数据转换成字符串
data_str = ?tr(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行 # -------关键就在于这一坨代码-------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!' conn.send(response)
conn.close()

完美解决了不同URL返回不同内容的问题。

但是我们不仅仅是想返回几个字符串,我们想给浏览器返回完整的HTML内容,这又该怎么办呢?接着走!


返回具体的HTML文件

首先我们要知道:不管是什么内容,最后都是转换成字节数据发送出去的.

那么,我们可以以二进制形式打开HTML文件,读取文件的数据,然后再发送给浏览器.

from socket import *

sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() # 将返回不同的内容部分封装成函数
def index(url):
with open('index.html', 'rb') as f: return f.read() def home(url):
with open('home.html', 'rb') as f: return f.read() # 定义一个url和实际要执行的函数的对应关系
url_list = [
('/index', index),
('/home', home)
] while True:
# ------------ 建立连接 接收消息 ------------
conn, addr = sk.accept()
data = conn.recv(8096) # ------------ 对客服端发来的消息做处理 ------------
# 把收到的字节类型的数据转换成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割 # ------------ 业务逻辑处理部分 ------------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!' # ----------- 回复响应消息 -----------
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行
conn.send(response)
conn.close()

没错,还有问题!

这网页是能够显示出来了,但都是静态的啊,页面的内容不会变化,我们想要的是动态网站!


让网页动态起来

思路:使用字符串替换功能来实现这个需求.

这里我们是使用当前时间来模拟动态的数据

login函数对应文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>login</title>
</head>
<body>
<h1>登录页面</h1>
<h1>This is login page!</h1>
<a href="https://blog.csdn.net/qq_41964425/article/category/8083068" target="_blank">CSDN</a> <p>时间:@@xx@@</p> <!--提前定义好特殊符号-->
</body>
</html>

Python代码:

from time import strftime
from socket import * sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen() # 将返回不同的内容部分封装成函数
def index(url):
with open('index.html', 'rb') as f: return f.read() def home(url):
with open('home.html', 'rb') as f: return f.read() # 实现动态网页
def login(url):
now_time = str(strftime('%F %T'))
with open('login.html', 'r', encoding='UTF-8') as f:
s = f.read()
# 在网页中定义好特殊符号,用动态的数据去替换定义好的特殊符号
s = s.replace('@@xx@@', now_time)
return bytes(s, encoding='UTF-8') # 定义一个url和实际要执行的函数的对应关系
url_list = [
('/index', index),
('/home', home),
('/login', login),
] while True:
# ------------ 建立连接 接收消息 ------------
conn, addr = sk.accept()
data = conn.recv(8096) # ------------ 对客服端发来的消息做处理 ------------
# 把收到的字节类型的数据转换成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割 # ------------ 业务逻辑处理部分 ------------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!' # ----------- 回复响应消息 -----------
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 响应状态行
conn.send(response)
conn.close()

服务器和应用程序

对于开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序.

服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理;应用程序则负责具体的逻辑处理.

为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py等。不同的框架有不同的开发方式,但是无论如何,开发出来的应用程序都要和服务器程序配合,才能为用户提供服务。

如此,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论是对于服务器还是框架,都是不好的。

这时候,标准化就变得尤为重要,我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么它们就可以配合使用。一旦标准确定,双方各自实现。

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

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

wsgiref 模块

使用wsgiref模块来替换我们自己写的Web框架的socket server部分:

login函数对应文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>login</title>
</head>
<body>
<h1>登录页面</h1>
<h1>This is login page!</h1>
<a href="https://blog.csdn.net/qq_41964425/article/category/8083068" target="_blank">CSDN</a> <p>时间:@@xx@@</p> <!--提前定义好特殊符号-->
</body>
</html>

Python代码:

from time import strftime
from wsgiref.simple_server import make_server # 将返回不同的内容部分封装成函数
def index(url):
with open('index.html', 'rb') as f: return f.read() def home(url):
with open('home.html', 'rb') as f: return f.read() def login(url):
"""实现动态网页"""
now_time = str(strftime('%F %T')) # 获取当前格式化时间
with open('login.html', 'r', encoding='UTF-8') as f:
s = f.read()
# 在网页中定义好特殊符号,用动态的数据去替换定义好的特殊符号
s = s.replace('@@xx@@', now_time)
return bytes(s, encoding='UTF-8') # 定义一个url和实际要执行的函数的对应关系
url_list = [
('/index', index),
('/home', home),
('/login', login),
] def run_server(environ, start_response):
# 设置HTTP响应的状态码和头消息
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
url = environ['PATH_INFO'] # 取到用户输入的url func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!' return [response, ] if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8080, run_server)
print('Wsgiref has started...')
httpd.serve_forever()

【Django】Web框架本质的更多相关文章

  1. Django:web框架本质

    一,web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 1,自定义web框架 impo ...

  2. [oldboy-django][1初始django]web框架本质 + django框架 + ajax

    web框架本质 浏览器(socket客户端) - 发送请求(ip和端口,url http://www.baidu.com:80/index/) - GET 请求头(数据请求行的url上: Http1. ...

  3. Web框架本质及第一个Django实例

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...

  4. Web框架本质及第一个Django实例 Web框架

    Web框架本质及第一个Django实例   Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...

  5. WEB框架本质和第一个Django实例

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 总的来说:Web框架的本质就是浏览 ...

  6. Django之Web框架本质及第一个Django实例

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...

  7. Django框架----Web框架本质

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...

  8. Django 基础 web框架本质

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. import socket sk ...

  9. Django框架01 / http协议、web框架本质

    Django框架01 / http协议.web框架本质 目录 Django框架01 / http协议.web框架本质 1.http协议 1.1 http协议简介 1.2 什么是http协议 1.3 H ...

  10. django -- web框架的本质

    web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 下面代码是一个简单的socket服 ...

随机推荐

  1. ScrollView反弹效果的实现

    发现非常多APP的界面都能够滑动,QQ.微信等等,自己琢磨了下.效果例如以下: 代码:ScrollView package com.wsj.wsjdemo; import android.conten ...

  2. Swift,Objective-C语言性能对照測试

    原文发表于踏得网 Swift包括了非常多现代语言特性尤其是从一些脚本语言如Javascript/Ruby中汲取了营养. 此外苹果公布Swift时,使用特别选用的一些样例来宣称Swift性能对于Ojbe ...

  3. java webproject中logback换配置文件的路径

    本人小站点:   http://51kxd.com/  欢迎大家不开心的时候訪问訪问,调节一下心情 web.xml中配置: <!-- windows  logback.xml文件跟web容器(比 ...

  4. hdoj--1301--Jungle Roads(克鲁斯卡尔)

    Jungle Roads Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tot ...

  5. svn服务器的搭建过程 主要为服务端

    yum -y install subversion 查看版本信息 svnserve --version 创建svn仓库目录 mkdir -p /var/svn/svnrepos 创建svn仓库 svn ...

  6. Python TurtleWorld configuration and simple test

    TurtleWorld provides a set of functions for drawing lines by steering turtles around the screen. You ...

  7. Google Codejam 2016 Round1A Problem C BFFs 简单图论

    链接 Google Codejam 2016 Round1A Problem C BFFs 题意 n个小朋友要坐成一个圈.每个小朋友心中都有一个Best Friend Forever.要保证每个人的左 ...

  8. C# fixed语句固定变量详解

    相信很多人在这样或那样的项目中,或者无意间看到了fixed语句块,看到之后你肯定会疑问: 1,这个fixed关键字是做什么用的? 2,什么情况下需要该关键字? 3,这个关键字该怎么用? 我相信解决了上 ...

  9. PostgreSQL Replication之第四章 设置异步复制(5)

    4.5 使流复制更健壮 当连接到master时,slave要做的第一件事情是赶上master.但是,这会一直工作吗?我们已经看到,我们可以使用由基于流和基于文件组成的混合设置.这给了我们一些额外的安全 ...

  10. affe(8) solver 优化方法

    上文提到,到目前为止,caffe总共提供了六种优化方法: Stochastic Gradient Descent (type: "SGD"), AdaDelta (type: &q ...