笔记整理:用socket实现Web开发框架流程
基础概念
socket: socket是一种通信机制,通过绑定IP地址和端口产生一个通信链,实现计算机之间的通信。网络中不同的计算机之间进行通信必须经过IP地址和端口,为了降低网络通信开发的复杂度,人们在TCP/IP4层结构中的应用层与传输层之间加了一层,这个层就是socket层。它把复杂的TCP/IP进行了封装,并提供了一组服务的接口。
**Web服务:**Web服务本质上是由socket服务端向socket客户端提供HTTP响应,而浏览器就是一个socket客户端。网络中的服务器主机会提供一种或多种服务,每一种服务打开一个socket,并绑定到一个端口上,也就是说不同的端口对应着不同的服务,客户端向那个端口发送请求,就会得到相应的服务。当用户在浏览器地址栏输入网址(URL)并确定时,这个动作称为发送Web请求,网络中会有一台与网址相对应的服务器按用户的请求做出相应,把请求资源发送给用户。这台接收Web请求并作出相应的服务器成为Web服务器,它把用户请求的资源以HTML文件的形式传递给用户的浏览器。
**Web应用的本质:**Web应用主要做的事情就是发送HTML文件到浏览器,其核心功能则通过socket服务完成。因此Web服务器本质上是一个socket服务端,浏览器本质上是一个socket客户端。
Web开发框架的运行方式
根据上文对socket服务的描述我们可以简单地将Web通信流程理解成三个步骤:
浏览器向服务器发出请求 ——> 服务器接收到请求信息并根据一定的逻辑确定需要反馈的内容 ——> 服务器将反馈的内容传递给浏览器
建立socket服务
想要用socket实现以上流程,我们第一步应该做的就是在浏览器(客户端)与服务器之间建立联系,否则双方不能接收对方的请求或者反馈就一切都没有意义:
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",8000))
sk.listen()
客户端与服务端二者通信
以上代码就已经在客户端与服务端搭建了联系,浏览器输入http://127.0.0.1:8000并确定就可以理解为一个手机(客户端)拨通了一个客服(服务器)的电话。而代码sk.listen()只是表示客服上线了在带命中,并不表示客户端只要发出请求服务器就能接收到,接下来的代码才是二者的通信:
while True:
conn,addr = sk.accept()
data = conn.recv(1024)
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
以上代码已经实现了客户端和服务端的通信,但是data和conn.send()所发送的内容都是HTTP请求或响应的消息。其格式如下:
代码块中conn.send()的作用是向客户端发送消息,所以发送消息的第一句只能是按照HTTP响应消息的格式发送响应头第一行信息,此时的浏览器界面上是看不到任何消息的。响应头的其他字段和值可以省略,所以想要向客户端发送其他消息再调用一次conn.send()即可。
Web开发的核心功能
如果说上一部分是让客户端与服务端之间拨通了电话,相互说了一声“喂~”,那么接下来这部分就是二者实际的交互。服务器的背后虽然由研发维护人员,但是不可能真的由一个客服人员在线等待客户端的请求再反馈。这必定是用代码来写的一个逻辑关系——当服务端接收到客户端的请求后做出将返回什么样响应的逻辑关系,而这个逻辑关系就是Web开发的核心功能。
import socket
def indexes(url):
with open("index.html","r",encoding="utf-8") as f:
rd = f.read()
rd = rd.replace("$@index$@","首页")
return bytes(rd,encoding="utf-8")
def tests(url):
with open("test.html","r",encoding="utf-8") as f:
rd = f.read()
rd = rd.replace("$@test$@","测试")
return bytes(rd,encoding="utf-8")
def fun404(url):
ret = "<h1>not found!</h1>"
return bytes(ret,encoding="utf-8")
url_func = [
("/index/",indexes),
("/test/",tests)
]
sk = socket.socket()
sk.bind(("127.0.0.1",8000))
sk.listen()
while True:
conn,addr = sk.accept()
data = conn.recv(1024)
if not data:
continue
data_str = str(data)
line = data_str.split("\r\n")
v1 = line[0].split()
url = v1[1]
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
func = None
for i in url_func:
if i[0] == url:
func = i[1]
break
if not func:
func = fun404
rep = func(url)
conn.send(rep)
conn.close()
再附上代码块中需要的两个HTML文件
<html lang="en">
<head>
<meta charset="utf-8">
<title>index页面</title>
</head>
<body>
<h1>$@index$@</h1>
</body>
</html>
test.html文件同上index.html,不在重复。这里简单地说明一下,之所以在主题一级标签的内容两边都加上$@符号是当作占位符使用,毕竟python的占位符%或者{}在其他语言中都有另外的意义。
Web框架的3个核心功能功能
本文打这里,用socket实现Web开发框架就已经全部完成了。概括起来就是python中Web框架一般实现的3个核心功能:
1、收发消息(socket功能)
2、根据用户不同路径执行不同的函数
3、从HTML文件中取出内容,并且完成字符串的替换
目前python主流的Web开发框架有Django、Tornado和Flask三种,也是各有不同地实现上述核心功能的两到三种。如Django就是实现了第二三种功能,这两种功能可以很容易地通过编写代码或配置来实现,第一种功能则是以第三方工具来实现的。
符串的替换
目前python主流的Web开发框架有Django、Tornado和Flask三种,也是各有不同地实现上述核心功能的两到三种。如Django就是实现了第二三种功能,这两种功能可以很容易地通过编写代码或配置来实现,第一种功能则是以第三方工具来实现的。
图片来源:拍摄于书籍《Django实战Python Web典型模块与项目开发》(张晓 著)
|