zoukankan      html  css  js  c++  java
  • 搭建简易的WebServer(基于pyhton实现简易Web框架 使用socket套接字)

     1. 使用web底层socket的方式实现简易服务器的搭建,用来理解学习

    # 1、导入socket模块
    import socket
    import re
    import gevent
    import sys
    # 破解,让gevent 识别耗时操作
    from gevent import monkey
    monkey.patch_all()
    import Application
    
    
    class HttpServer(object):
    
        # 用来初始化 套接字
        def __init__(self, port):
            
            # 2、创建tcp套接字
            tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 3、设置地址重用
            tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
            # 4、绑定端口
            tcp_server_socket.bind(("", port))
            # 5、设置监听,最大允许客户端连接数128(套接字有主动变为被动)
            tcp_server_socket.listen(128)
            # 套接字,保存实例属性中
            self.tcp_server_socket = tcp_server_socket
    
        # 启动Web服务器,并且接受客户端连接
        def start(self):
            # 6、等待客户端连接(能够接受多个客户端连接)
            while True:
                # 7、定义函数,实现客户端信息接收和响应
                new_client_socket, ip_port = self.tcp_server_socket.accept()
                print("[新客户端来了]:", ip_port)
                # request_handler(new_client_socket)
                g1 = gevent.spawn(self.request_handler, new_client_socket)
                # 因为接收客户端链接使用的是While True  死循环,主线程不会退出,协程一定会执行完毕
                # g1.join()
    
        def request_handler(self, new_client_socket):
    
            # ​ 8、接收浏览器请求,并判断请求是否为空
            recv_data = new_client_socket.recv(1024)
            if not recv_data:
                print("浏览器可能已经关闭!~")
                new_client_socket.close()
                return
            # 把接受的内容解码
            request_text = recv_data.decode()
            # 根据
     拆分字符串,目的:得到第一行
            request_list = request_text.split("
    ")
            # request_list[0] 就是我们的请求行
            # print(request_list[0])
            ret = re.search(r"s(.*)s", request_list[0])
            # 判断报文是否有误
            if not ret:
                print("浏览器请求的报文格式错误!")
                new_client_socket.close()
                return
    
            # 获取路径
            path_info = ret.group(1)
            # print(path_info)
            # 访问默认的页面
            if path_info == "/":
                path_info = "/index.html"
            # ​ 9、拼接响应报文
    
            # 判断如果是动态资源 交给框架处理
            if path_info.endswith(".html"):
                # 字典存储 用户的请求信息
                env = {
                    "PATH_INFO": path_info
                }
    
                status, headers, response_body = Application.app(env)
    
                # 使用框架返回的数据拼接响应报文
                response_line = "HTTP/1.1 %s
    " % status
    
                response_header = ""
                for header in headers:
                    response_header += "%s: %s
    " % header
    
                response_data = response_line + response_header + "
    " + response_body
                new_client_socket.send(response_data.encode())
                new_client_socket.close()
            # 否则认为是静态资源
            else:
                #  9.2 响应头
                response_header = "Server:PythonWS1.0
    "
                #  9.3 空行
                response_blank = "
    "
                #  9.4 响应体
                # response_content = "HelloWorld!
    "
                # 打开文件,并且读取内容,然后把读取的内容返回给客户端
    
                try:
                    with open("static"+path_info, "rb") as file:
                        # response_content 是二进制类型
                        response_content = file.read()
                except Exception as e:
                    #  9.1 相应行
                    response_line = "HTTP/1.1 404 Not Found
    "
                    response_content = "Error ! %s" % str(e)
                    # 对返回的内容进行编码
                    response_content = response_content.encode()
                else:
                    #  9.1 相应行
                    response_line = "HTTP/1.1 200 OK
    "
                finally:
                    # ​ 10、定义变量保存响应报文内容
                    response_data = (response_line + response_header + response_blank).encode() + response_content
                    # ​ 11、发送响应报文给客户端浏览器
                    new_client_socket.send(response_data)
    
                    # ​ 12、关闭此次连接的套接字
                    new_client_socket.close()
    
    
    def main():
    
        # sys.argv 可以获取终端启动程序的时候的启动参数
        # sys.argv 返回一个列表,列表中依次保存我们输入的参数内容
        # print(sys.argv)
        if len(sys.argv) != 2:
            print("服务器启动失败,参数的格式:python3 HttpServer.py 端口号")
            return
    
        # 判断端口号,不能是一个字符串,应该是纯数字
        if not sys.argv[1].isdigit():
            print("服务器启动失败,端口号应该是纯数字!")
            return
        # 获取端口号
        port = int(sys.argv[1])
    
        # 实例化 HttpServer 对象
        httpserver = HttpServer(port)
    
        # 启动服务器
        httpserver.start()
    
    
    if __name__ == '__main__':
    
        main()
     2. 建立子应用app用来接收请求
    
    """web框架  web应用程序"""
    import time
    
    
    def get_time():
        """当用户请求/gettime.html执行当前方法"""
        return time.ctime()
    
    def index():
        """当用户请求/index.html 执行"""
        # 1 读取模板文件
        with open("template/index.html") as file:
            html_data = file.read()
    
        # 2 查询数据库
        data_from_mysql = "have fun"
    
        # 3 使用从数据库中查询出来的数据 替换 模板变量
        html_data = html_data.replace("{%content%}", data_from_mysql)
    
        return html_data
    
    
    def center():
        """当用户请求/center.html 执行"""
        # 1 读取模板文件
        with open("template/center.html") as file:
            html_data = file.read()
    
        # 2 查询数据库
        data_from_mysql = "have fun"
    
        # 3 使用从数据库中查询出来的数据 替换 模板变量
        html_data = html_data.replace("{%content%}", data_from_mysql)
    
        return html_data
    
    # 路由列表  django框架添加路由的方式
    route_list = [
        ('/gettime.html', get_time),
        ('/center.html', center),
        ('/index.html', index)
    ]
    
    def app(env):
        # 接收  取出用户的信息
        path_info = env['PATH_INFO']
        print("接收到用户的动态资源请求 %s" % path_info)
        # if path_info == '/gettime.html':
        #     # 状态 响应头 响应体
        #     return '200 OK', [('Server', 'PWS5.0')], get_time()
        # elif path_info == '/index.html':
        #     return '200 OK', [('Server', 'PWS5.0')], index()
        # elif path_info == '/center.html':
        #     return '200 OK', [('Server', 'PWS5.0')], center()
        # 将用户请求路径 和 路由列表中每一个进行比较 如果一致 就执行对应的函数代码
        for path, func in route_list:
            if path_info == path:
                return '200 OK', [('Server', 'PWS5.0')], func()
        else:
            # 状态 响应头 响应体
            return '404 Not Found',[('Server', 'PWS5.0')],"response body from app"
  • 相关阅读:
    入门5查询生成器
    入门4关键概念
    入门3请求处理
    入门2应用结构
    入门1-基础入门
    PHP代码规范
    Bug总结
    数据结构与常用集合总结
    CSS之使用display:inline-block来布局
    nginx,lvs,haproxy+keepalived区别
  • 原文地址:https://www.cnblogs.com/yanguhung/p/10145766.html
Copyright © 2011-2022 走看看