所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
下面 我们开始自定义一个框架,逐步的完善它。
1、最基础,的web框架
import socket
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建tcp连接
sk.bind(('localhost',9900))#绑定本机和端口号
sk.listen()
while True:
conn,addr=sk.accept()
rt=conn.recv(1024)
print(rt.decode('utf-8)
conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
conn.send(b'ok')
#往页面上发送消息
conn.send('王八蛋'.encode('utf-8'))
conn.close()
控制台:
网页上:
2、从上图可以看出网页上的文字乱码了,接下来我们解决乱码问题
import socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sc.bind(('127.0.0.1',9098))
sc.listen()
while True:
conn,addr=sc.accept()
rt=conn.recv(1024).decode('utf-8')
print(rt)
# 结局乱码
msg1='http/1.1 200 ok \r\n'.encode('utf-8')
msg2='Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3='\r\n'.encode('utf-8')
msg4='这是一首简单的小情歌'.encode('utf-8')
#发送解决乱码的msg
conn.send(msg1)
conn.send(msg2)
conn.send(msg3)
conn.send(msg4)
conn.send(b'<h1>hello</h1>' )
conn.close()
页面(看乱码已解决):
3、获得路径
import socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sc.bind(('127.0.0.1',9098))
sc.listen()
while True:
conn,addr=sc.accept()
ret=conn.recv(1024)
print(ret.decode('utf-8'))
#这里我们把接收到的内容切割,直到我们得到想要的路径
#首先以回车符+换行符切割,之后取第一个,再以空格切割取第二个
data1=str(ret,encoding='utf-8').split('\r\n')[0].split(' ')[1]#ret这个返回值,如果在之前decode了,可以不用强转为字符串
print(data1)
#三种路径,分别是home,index,和未知
if data1 =='/home':
msg='这是{}页面'.format(data1).encode('utf-8')
elif data1=='/index':
msg='this is a{} page'.format(data1).encode('utf-8')
else:
msg='sorry not find'.encode('utf-8')
#解决乱码
conn.send('http/1.1 200 ok\r\n'.encode('utf-8'))
conn.send('Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8'))
conn.send('\r\n'.encode('utf-8'))
conn.send(msg)
conn.close()
获得的客户端的消息,也就是要切割的东西:
index路径下的网页
home路径下的网页
未知网页:
4、页面内容,上升为函数(必要的)
如果有很多很多路径要判断怎么办?难道要挨个写if判断?
当然不用,我们将所出现的可能的封装为一个一个的函数,通过切割出来的路径,来分别执行对应的 函数
import socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sc.bind(('127.0.0.1',9098))
sc.listen()
while True:
conn,addr=sc.accept()
ret=conn.recv(1024)
print(ret.decode('utf-8'))
#获得路径
data=str(ret,encoding='utf-8').split('\r\n')[0]
data1=data.split(' ')[1]
print(data1)
#三个函数,对应三个页面的内容
def func1():
return '这是{}页面'.format(data1).encode('utf-8')
def func2():
return 'this is a {} page'.format(data1).encode('utf-8')
def func3():
return 'sorry not find'.encode('utf-8')
#将三个函数放在列表中
func=None
ls=[('/index',func1),('/home',func2),('',func3)]
#func赋值,
for i in ls:
if i[0]==data1:
func=i[1]
break
#对应路径对应相应的函数
if func:
msg=func()
else:
msg=func3()
#解决乱码问题
conn.send('http/1.1 200 ok\r\n'.encode('utf-8'))
conn.send('Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8'))
conn.send('\r\n'.encode('utf-8'))
#显示对应路径
conn.send(msg)
conn.close()
结果和上图无差别
5、上面我们返回的是字符串,接下来我们返回html页面
写三个简单的html文件:
内容也很简单:
index:
home:
error:
import socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sc.bind(('127.0.0.1',9098))
sc.listen()
while True:
conn,addr=sc.accept()
ret=conn.recv(1024)
print(ret.decode('utf-8'))
#获得路径
data=str(ret,encoding='utf-8').split('\r\n')[0]
data1=data.split(' ')[1]
print(data1)
#三个函数,对应三个html的内容
def func1():
with open('index.html',mode='rb')as f:
ret=f.read()
return ret
def func2():
with open('home.html', mode='rb')as f:
ret = f.read()
return ret
def func3():
with open('error.html', mode='rb')as f:
ret = f.read()
return ret
#将三个函数放在列表中
func=None
ls=[('/index',func1),('/home',func2),('',func3)]
#func赋值,
for i in ls:
if i[0]==data1:
func=i[1]
break
#对应路径对应相应的函数
if func:
msg=func()
else:
msg=func3()
#解决乱码问题
conn.send('http/1.1 200 ok\r\n'.encode('utf-8'))
conn.send('Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8'))
conn.send('\r\n'.encode('utf-8'))
#显示对应的html文件
conn.send(msg)
conn.close()
home路径下的
5、动态的页面,也就是代码中改变html中的内容
import socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sc.bind(('127.0.0.1',9098))
sc.listen()
# 三个函数,对应三个html的内容
def func1():
with open('index.html', mode='r', encoding='utf-8')as f:
ret = f.read()
ret = ret.replace('xxoo', data1).encode('utf-8')#将index文件中的xxoo改为路径名字
return ret
def func2():
with open('home.html', mode='rb')as f:
ret = f.read()
return ret
def func3():
with open('error.html', mode='rb')as f:
ret = f.read()
return ret
#将三个函数放在列表中
func=None
ls=[('/index',func1),('/home',func2),('',func3)]
while True:
conn,addr=sc.accept()
ret=conn.recv(1024)
# print(ret.decode('utf-8'))
#获得路径
data=str(ret,encoding='utf-8').split('\r\n')[0]
data1=data.split(' ')[1]
print(data1)
#func赋值,
for i in ls:
if i[0]==data1:
func=i[1]
break
#对应路径对应相应的函数
if func:
msg=func()
else:
msg=func3()
#解决乱码问题
conn.send('http/1.1 200 ok\r\n'.encode('utf-8'))
conn.send('Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8'))
conn.send('\r\n'.encode('utf-8'))
#显示对应的html文件
conn.send(msg)
conn.close()
结果对比:
and
总结
1.web框架的本质:socket 服务端 与浏览器的通讯。
2.socket 服务端功能可以划分为3部分:
- a.负责与浏览器收发消息(socket)在python中有专门的框架
wsgiref/uWsgi/gunicorn… - b.根据用户访问不同的路径执行不同的函数
- c.从HTML中读取出内容,并且完成字符串的替换
3.Python中 web 框架的分类: - 按照2上面的功能分类
(1).框架自带a,b,c 功能 ----> Tornado
(2).框架自带b,c,使用第三方的a ---->Django
(3).框架自带b,使用第三方的a,c ---->Flask
-按照另一个维度划分
(1).Diango ,Tornado–>大而全(做一个网站用到的技术都有)
(2).其他 例如 Flask 轻量级只封装了核心功能。
a部分和b、c 部分通讯需要遵守WSGI 协议。