首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >python之web服务器

python之web服务器

作者头像
用户8639654
修改2021-08-17 18:18:04
修改2021-08-17 18:18:04
3.1K0
举报
文章被收录于专栏:云计算运维云计算运维

这是一篇写在正式引入web框架之前的总结,也是对前面的一个总结,认识会深刻一点,看起来会更加容易理解。

所以有的变化都是人的需求在不断发生变化,为了追求卓越。(括号中的ver-**指的是服务器版本,自己理解的,并不是官方定义的)

(ver-0.1)在前面的文章中,客户端与服务端最早只是以文字形式进行交互;

(ver-0.2)然后觉得文字形式太过单调,于是将服务器返回给客户端的信息按照一种特殊格式进行编写,就形成了网页信息(内容丰富多样)。

到这里服务器返回给客户端的信息都是在服务器代码中直接编写的,信息维护起来就异常困难了。于是就想把回复的信息单独拿出来,客户端请求什么,会调用什么,这样服务器代码内容不用怎么去管,只需要编写客户端请求的信息对应的返回内容即可。

(ver-0.3)这个时候html文件就单独抽离出来了,通过正则表达式抽取客户端请求的内容去调用对应的文件。

(ver-0.4)人的需求又在发生变化,客户端不在满足得到的只是静态文件,想要拿到最新的动态信息,比如访问服务器可以得到最新的时间信息,可以得到最新的投票信息等等,这个时候就需要服务器能调用可执行的py文件(调用某个文件,就执行某个函数)

到这里,问题又来了。文件格式那么多,服务器还要去解析客户请求的是以.html结尾的文件还是以.py结尾的文件。而且客户端的请求可能只是两三行代码就行完成的事,却要写一个py文件,一千个请求就要写一千个Py文件,管理起来也很麻烦。

(ver-0.5)这个时候框架就被引出来了。服务器只负责接收客户端请求,然后将请求的信息结果返回给客户就可以了。至于客户端请求的信息是静态的还是动态的,响应的信息怎么完成的,通通不用管。这个服务器的负担就少了很多,只需要写相应的代码即可,但这里(ver0.4)不同的是0.4的版本中,一个响应是一个html或者py文件.这个版本中客户端请求都用一个文件编写,一个请求对应一个类,具体请求信息对应相应的函数。而且网页的展示也是只需要将拿到的数据进行渲染就可以返回给服务器,服务器在返回给客户端。一个框架就出来了,各司其职。

这样说可能没什么概念,好像只要我自己懂了,还是把代码写出来吧。

ver-0.1 (这个版本服务器响应的就是文字,还是网页形式)

代码语言:javascript
复制
#coding = utf-8
from socket import *
from threading import Thread
import sys
def newsocket_(newsockets,ip_port):   #第三步,新的套接字生成。
        def senddata_():
            while True:
                newsockets.send((input("<<")).encode("gb2312"))   #可以给客户端发消息
        def recvdata_():      
            while True:
                recvdata = newsockets.recv(1024)     #第四步,等待客户端的请求信息
                if len(recvdata) > 0:
                    sys.stdout.write('\r%s%s%s\n' % (">>",ip_port,recvdata.decode("gb2312")))
                    sys.stdout.flush()
                    sys.stdout.write("\r%s" % ('<<'))
                    sys.stdout.flush()
                else:
                    print("客户端已关闭。。。")   #客户端断开连接
                    break
            newsockets.close()
        threadchild2 = Thread(target=senddata_, )   #为了同时处理收消息和发消息
        threadchild1 = Thread(target=recvdata_, )
        threadchild1.start()
        threadchild2.start()
        threadchild1.join()
        threadchild2.join()
def main():
    sersocket = socket(AF_INET, SOCK_STREAM)   #第一步创建套接字
    sersocket.bind(("", 7788))    #绑定端口
    sersocket.listen(5)      #设置监听
    while True:
        newsockets, ip_port = sersocket.accept()    #第二步,等待客户端的连接
        threads = Thread(target=newsocket_, args=(newsockets,ip_port))
        threads.start()    #接收到客户端的请求,创建新的套接字,分配一个线程,监听套接字继续等待
if __name__ == '__main__':
    main()

运行结果:

ver-0.2 (这个版本,整个网页都是在服务代码中构造)

代码语言:javascript
复制
#coding = utf-8
from socket import *
from threading import Thread
def recvsocket(client_socket):
    recvdata = client_socket.recv(1024)     #第四步,收到客户端请求,然后构造html页面元素,并返回给客户端
    print("request data:", recvdata)  #打印请求信息
    response_start_line = "HTTP/1.1 200 OK\r\n"   #【7-9】创造响应内容(响应状态,响应头,响应体),只有这样才能以网页的形式展示出来
    response_headers = "Server: pycharm server\r\n"
    response_boby = "hello"
    response = response_start_line + response_headers + "\r\n" + response_boby   #将构造的几部分和合并成响应报文
    client_socket.send(response.encode("utf-8"))   #发送给客户端
    client_socket.close()
if __name__ == '__main__':
    server = socket(AF_INET, SOCK_STREAM)  #第一步创建套接字
    server.bind(("", 7788))
    server.listen(5)
    while True:
        client_socket , ip_port = server.accept()   #第二步等待客户端请求连接
        print("%s:%s>>>正在连接中。。。"%ip_port)
        childthread = Thread(target=recvsocket , args=(client_socket,))
        childthread.start()     #第三步,创建新的套接字,分配线程

运行结果

ver-0.3 (这个版本,响应内容从服务器代码中单独抽离出来了。)

代码语言:javascript
复制
#coding = utf-8
from socket import *
from threading import Thread
import re
def recvsocket(client_socket):
    recvdata = client_socket.recv(1024)
    print("request data:", recvdata)     #到这里都是一样的
    recv_lines = recvdata.splitlines()    #这里开始有点不同,客户端可能不会访问主页,可能会有参数,这时要将GET请求的参数提取出来
    # for line in recv_lines:
    #     print(line)
    request_start_line = recv_lines[0]   #提取Get请求
    file_name = re.match(r"\w+ +(/[^ ]*) ",request_start_line.decode("utf-8")).group(1)    #匹配请求内容
    if "/" == file_name:
        file_name = "/index.html"
    try:
        file = open(HTML_ROOT_DIR + file_name,"rb")
    except IOError:
        response_start_line = "HTTP/1.1 404 Not Foud\r\n"
        response_headers = "Server: pycharm server\r\n"
        response_boby = "The file is not fund!"
    else:
        file_data = file.read()
        file.close()
        response_start_line = "HTTP/1.1 200 OK\r\n"
        response_headers = "Server: pycharm server\r\n"
        response_boby = file_data.decode("utf-8")
    response = response_start_line + response_headers + "\r\n" + response_boby
    print("response data:", response)
    client_socket.send(response.encode("utf-8"))
    client_socket.close()
if __name__ == '__main__':
    HTML_ROOT_DIR = "./html"    #本地已经建好的html文件
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(("", 7788))
    server.listen(5)
    while True:
        client_socket , ip_port = server.accept()
        print("%s:%s>>>正在连接中。。。"%ip_port)
        childthread = Thread(target=recvsocket , args=(client_socket,))
        childthread.start()

响应内容信息:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>初识web服务器</title>
</head>
<body>
welcome The first webserver
</body>
</html>

运行结果:

ver-0.4 (这个版本服务器既可以返回静态页面,还可以返回动态的页面信息了)

服务器代码部分

代码语言:javascript
复制
#coding = utf-8
from socket import *
from threading import Thread
import re
import sys
HTML_ROOT_DIR = "./html"
WSGI_PYTHON_DIR = "./wsgipython"
sys.path.insert(1 , WSGI_PYTHON_DIR)

class Recvsocket(object):
    def __init__(self):
        self.server = socket(AF_INET, SOCK_STREAM)
        self.server.bind(("", 7788))
        self.server.listen(5)

    def recvsocket(self):
        while True:
            self.client_socket, ip_port = self.server.accept()
            print("%s:%s>>>正在连接中。。。" % ip_port)
            childthread = Thread(target=self.newsocket, args=(self.client_socket,))
            childthread.start()

    def start_response(self,status,server_headers):
        response_header = "HTTP/1.1 " + status + "\r\n"
        for header in server_headers:
            response_header += "%s:%s\r\n"%header
        self.response_header = response_header    #这里得到是响应状态和响应头

    def newsocket(self , client_socket):
        recvdata = self.client_socket.recv(1024)
        print("request data:", recvdata)
        recv_lines = recvdata.splitlines()
        request_start_line = recv_lines[0]
        file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line.decode("utf-8")).group(1)
        if file_name.endswith(".py"):
            m = __import__(file_name[1:-3])        #1,这是最重要的一步,检测到py文件,提取出文件名,导入模块
            env = {}
            response_boby = m.application(env , self.start_response) #,2,这是很重要一步,调用导入模块的函数,并得m_time的结果
            response = self.response_header + "\r\n" + response_boby
        else:
            if "/" == file_name:
                file_name = "/index.html"
            try:
                file = open(HTML_ROOT_DIR + file_name , "rb")
            except IOError:
                self.response_start_line = "HTTP/1.1 404 Not Foud\r\n"
                self.response_headers = "Server: pycharm server\r\n"
                self.response_boby = "The file is not fund!"
            else:
                file_data = file.read()
                file.close()
                self.response_start_line = "HTTP/1.1 200 OK\r\n"
                self.response_headers = "Server: pycharm server\r\n"
                self.response_boby = file_data.decode("utf-8")
            response = self.response_start_line + self.response_headers + "\r\n" + self.response_boby
        print("response data:", response)    #不管是静态页面还是动态的,都要返回,与第一个if条件平行
        client_socket.send(response.encode("utf-8"))
        client_socket.close()


if __name__ == '__main__':
    test = Recvsocket()
    test.recvsocket()

响应信息代码

代码语言:javascript
复制
#coding = utf-8
import time
def application(env,start_response):
    status = "200 OK"
    server_headers = [
        ("server", "pycharm server")
    ]
    start_response(status,server_headers)    #这也是很重要的一步,返回服务器函数
    return time.ctime()

运行结果:

但是在这里响应头都还是在服务器里面编写,并没有完全抽离,而且文件编写复杂,每个请求都要写了个文件,内存占用大。

ver-0.5 (这个版本开始,框架开始出来了,引入WSGI协议,服务器的接口和应用的接口都用这个协议编写,统一调动appplication函数,将多个响应的文件,抽象成对象,根据客户端请求内容调用对应的函数。)

服务器代码部分:

代码语言:javascript
复制
#coding = utf-8
from socket import *
from threading import Thread
import re
import sys
HTML_ROOT_DIR = "./html"
WSGI_PYTHON_DIR = "./wsgipython"
sys.path.insert(1 , WSGI_PYTHON_DIR)

class Recvsocket(object):
    def __init__(self,application):
        self.server = socket(AF_INET, SOCK_STREAM)
        self.server.bind(("", 7788))
        self.server.listen(5)
        self.app = application

    def recvsocket(self):
        while True:
            self.client_socket, ip_port = self.server.accept()
            print("%s:%s>>>正在连接中。。。" % ip_port)
            childthread = Thread(target=self.newsocket, args=(self.client_socket,))
            childthread.start()

    def start_response(self,status,server_headers):  #与框架的一个往返函数,响应头和响应体,会从框架那边返回过来
        response_header = "HTTP/1.1 " + status + "\r\n"
        for header in server_headers:
            response_header += "%s:%s\r\n"%header
        self.response_header = response_header

    def newsocket(self , client_socket):
        recvdata = self.client_socket.recv(1024)
        print("request data:", recvdata)
        recv_lines = recvdata.splitlines()
        request_start_line = recv_lines[0]
        file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line.decode("utf-8")).group(1)
        method = re.match(r"(\w+) +/[^ ]* " , request_start_line.decode("utf-8")).group(1)
        env = {
            "PATH_INFO":file_name,
            "METHOD":method
        }
        response_boby = self.app(env , self.start_response)
        response = self.response_header + "\r\n" + response_boby
        client_socket.send(response.encode("utf-8"))
        client_socket.close()

框架部分:

代码语言:javascript
复制
#coding = utf-8
import time
from webserverframe_01 import Recvsocket
HTML_ROOT_DIR = "./html"
class Application(object):
    def __init__(self,urls):
        self.urls = urls
    def __call__(self , env , start_response ):
        path = env.get("PATH_INFO","/")
        if path.startswith("/static"):  #如果请求的路径是这个,表示请求的内容是静态文件
            file_name = path[7:]
            try:
                file = open(HTML_ROOT_DIR + file_name, "rb")   #打开静态文件
            except IOError:       
                status = "404 Not Foud\r\n"     #表示没有找到
                headers = []
                start_response(status,headers)
                return  "The file is not fund!"
            else:
                file_data = file.read()    #找到了就读取
                file.close()
                status = "200 OK\r\n"
                headers = []
                start_response(status,headers)
                return file_data.decode("utf-8")

        for url , handler in self.urls:   #如果请求路径不是以/static开头,表示请求的是动态文件
            if path  == url:          #如果路径与我们设置的相同,则执行对应的函数,这个handler与第四个版本的m_time.py文件是一样的意思
                return handler(env,start_response)   #这里就和第四个版本的application一样
        status = "404 Not found"       #一个handler就是原先的一个py文件
        headers = []
        start_response(status,headers)
        return "Not Found "

def m_time(env,start_response):    #和application一样,具体执行。
    status = "200 OK"       #start_response非常重要,是要将响应头反馈给服务器的,由服务器完成拼接
    server_headers = [
        ("server", "pycharm server")
    ]
    start_response(status, server_headers) 
    return time.ctime()   #上一步返回服务器,然后继续回来执行任务,再将响应体反馈给服务器
def say_hello(env,start_response):
    status = "200 OK"
    server_headers = [
        ("server", "pycharm server")
    ]
    start_response(status, server_headers)  # 这也是很重要的一步,返回服务器函数
    return "hello world"

if __name__ == '__main__':
    urls = [
            ("/ctime",m_time),
            ("/sayhello",say_hello),
            ("/",say_hello)
        ]
    app = Application(urls)
    apphttp_server = Recvsocket(app)
    apphttp_server.recvsocket()

运行结果是一样。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档