根据之前学过的socket,我们可以理解所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。

1、半成品自定义web框架

import socket

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

sk.bind(("127.0.0.1", 80))

sk.listen()

while 1:

    conn, addr = sk.accept() # 建立连接

    data = conn.recv(8096) # 收发消息

    print(data)  # 将浏览器发来的消息打印出来

    conn.send(b"welcome") # 回复消息

    conn.close()

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 

这时候就有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。

HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?看我们打印出来的data信息。


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_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 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'


2、×××版自定义web框架

import socket

server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8080))

server.listen(5)

while 1:    

    conn,addr=server.accept()     

    data=conn.recv(1024) 

    print(data)   

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 给回复的消息加上响应状态行

    conn.send(b"welcome")

    conn.close()

3、实现不同的路径返回不同的内容

3.1 最low版

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8082))

server.listen(5)

while 1:

    conn,addr = server.accept()

    data = conn.recv(1024)

    data_str = str(data,encoding="utf-8")

    url = data_str.split("\r\n")[0].split()[1]

    if url == '/index':

        msg = b"This is index html"

    elif url == '/home':

        msg = b"This is home html"

    else:

        msg = b"404 Not Found!!!"

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

    conn.send(msg)

    conn.close()

3.2 函数版

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8082))

server.listen(5)

def index(url):

    return b"This is index html"

def home(url):

    return b"This is home html"

while 1:

    conn,addr = server.accept()

    data = conn.recv(1024)

    data_str = str(data,encoding="utf-8")

    url = data_str.split("\r\n")[0].split()[1]

    if url == '/index':

        msg = index(url)

    elif url == '/home':

        msg = home(url)

    else:

        msg = b"404 Not Found!!!"

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

    conn.send(msg)

    conn.close()

3.3 函数进阶

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8082))

server.listen(5)

def index(url):

    return b"This is index html"

def home(url):

    return b"This is home html"

url_list = [

    ("/index",index),

    ("/home",home),

]  # 定义一个url和实际要执行的函数的对应关系

while 1:

    conn,addr = server.accept()

    data = conn.recv(1024)

    data_str = str(data,encoding="utf-8")

    url = data_str.split("\r\n")[0].split()[1]

    func = None  # 定义一个保存将要执行的函数名的变量

    for i in url_list:

        if i[0] == url:

            func = i[1]

            break

    if func:

        msg = func(url)

    else:

        msg = b"404 not found!"

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

    conn.send(msg)

    conn.close()

4、服务端返回html网页

现在网页能够显示出来回复的内容了,但是都是我们写死的文本内容,生产环境中返回的都是专门的HTML文件呢?

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8082))

server.listen(5)

def index(url):

    with open('index.html','rb') as f:

        html_msg = f.read()

    return html_msg

def home(url):

    with open('home.html','rb') as f:

        html_msg = f.read()

    return html_msg

url_list = [

    ("/index",index),

    ("/home",home),

]

while 1:

    conn,addr = server.accept()

    data = conn.recv(1024)

    data_str = str(data,encoding="utf-8")

    url = data_str.split("\r\n")[0].split()[1]

    func = None # 定义一个保存将要执行的函数名的变量

    for i in url_list:

        if i[0] == url:

            func=i[1]

            break

    if func:

        msg=func(url)

    else:

        msg=b'404 Not found'       

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

    conn.send(msg)

    conn.close()

5、让页面动起来

现在网页能够显示写好的html文件了,但是生产环境中可能需要的是一些动态数据呢?

这里选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)

import socket

import time

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server.bind(("127.0.0.1",8082))

server.listen(5)

def index(url):

    with open('index.html','rb') as f:

        html_msg = f.read()

    return html_msg


def home(url):

    with open('home.html','r') as f:

        html_msg = f.read()

        now = str(time.time())

        html_msg = html_msg.replace("@@xx@@", now)

    return bytes(html_msg, encoding="utf8")

url_list = [

    ("/index",index),

    ("/home",home),

]

while 1:

    conn,addr = server.accept()

    data = conn.recv(1024)

    data_str = str(data,encoding="utf-8")

    url = data_str.split("\r\n")[0].split()[1]

    func = None # 定义一个保存将要执行的函数名的变量

    for i in url_list:

        if i[0] == url:

            func=i[1]

            break

    if func:

        msg=func(url)

    else:

        msg=b'404 Not found'

    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

    conn.send(msg)

    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开发环境用的就是这个模块来做服务器。