web服务器

服务器实现:

import socket
import re
import gevent
from gevent import monkey
import sys

monkey.patch_all()  # 自动切换  time.sleep recv accept 这些类型的函数在执行的时候会出现大量的耗时等待,所以让这部分闲置时间加以利用.


class HTTPServer:
    """web服务"""
    def __init__(self, port):
        """初始化操作"""
        # 1. 创建tcp服务器的socket  设置选项 绑定 监听
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('', port))  # 这里绑定的是服务器的固定端口,目的是让客户端能连接到了我的机器上面之后,可以找到我的这个程序
        server_socket.listen(128)

        # 将socket对象保存在 当前对象的属性中
        self.server_socket = server_socket

    def start(self):
        """启动服务"""
        while True:
            client_socket, client_addr = self.server_socket.accept()
            print('接受到%s的连接请求' % str(client_addr))
            # 为每个用户创建一个协程,并且启动 运行
            gevent.spawn(self.client_handler, client_socket)

    def client_handler(self, client_socket):
        """处理每个客户端的请求"""
        # 3.接收用户请求报文
        request_data = client_socket.recv(4096)
        # 解码 bytes  --> str
        request_data_str = request_data.decode()
        print(request_data)
        # ps:当用户的请求路径为/时候,表示其请求的是首页 homepage
        # 解析用户请求 获取到用户的资源请求路径
        # 正则匹配 只要是有/ 提取请求路径
        # GET /index.html
        result = re.search(r'^w+ (/S*)', request_data_str)
        # 判断是否提取成功  成功就取出这个值,不成功结束程序
        if not result:  # 路径为空
            print('请求报文格式错误')
            client_socket.close()
            # 结束 这种情况
            return

        # 尽量扁平结构 不要使用else缩进去,那样的是嵌套结构,这是小技巧
        # 获取请求路径  资源路径<static> + /index.html
        # 如果执行到这里,一定是匹配成功了,因为失败的已经跳过了
        path_info = result.group(1)

        # 4.返回用户指定的文件数据作为响应体   打包到响应报文中  static + /index.html
        # static 是文件夹,open打不开 潜规则 :所有的web服务器 不写路径默认是主页

        # 如果path_info 是/,返回 index首页
        if path_info == '/':
            path_info = '/index.html'
# ---------------------------动态资源请求-------------------------->
        # 如果是动态资源,需要调用框架来处理  python中.html结尾的就是动态资源
        env = {
            'PATH_INFO': path_info  # 实际情况下web服务器会将所有的请求信息放到字典中,env是字典类型参数
        }
        if path_info.endswith('.html'):
            import Application

            status, headers, response_body = Application.app(env)
            # 先拼接响应行
            response_line = 'HTTP/1.1 %s
' % status
            response_header = ''
            for name, value in headers:
                response_header += '%s:%s
' % (name, value)
            response_data = response_line + response_header + '
' + response_body
            client_socket.send(response_data.encode())
            client_socket.close()
# -------------------------静态资源的处理----------------------->
        else:
            # 静态资源的处理
            try:
                with open('static' + path_info, 'rb') as file:
                    html_data = file.read()  # html_data 是二进制bytes字节类型的数据
            except Exception as e:
                with open('static/404.html', 'rb') as file:
                    html_data = file.read()
                response_data = ('HTTP/1.1 404 Not Found
Server: PWS3.0Plus

').encode() + html_data
            else:
                # .encode()先把前面的str类型转成bytes字节类型,然后拼接
                response_data = ('HTTP/1.1 200 OK
Server: PWS3.0Plus

').encode() + html_data

            finally:
                # 统一最后发送
                client_socket.send(response_data)
                # 5.一次请求/响应 就关闭连接  --> 短连接
                client_socket.close()


def main():
    # 获取程序运行所需的参数<端口>   python3  1.py  8080  argv:['x.py',8080]
    if len(sys.argv) < 2:
        print('参数使用出错,应该是形如:python3 07-web服务器-命令行参数控制内部绑定的端口.py 8080')
    # 端口号是一个数字
    port = int(sys.argv[1])

    # 创建web服务
    http_server = HTTPServer(port)
    # 启动web服务
    http_server.start()


if __name__ == '__main__':
    main()
原文地址:https://www.cnblogs.com/huaibin/p/12101017.html