Python实现简易版web服务器

1.Python实现简易版web服务器

# coding = utf-8
import socket
import re
import threading
import multiprocessing
import gevent
import sys
from gevent import monkey
# monkey.patch_all()
# 采用多进程时,如果开启monkey.patch_all()会报错,报错信息如下:
# TypeError: Cannot serialize socket object


class HTTPServer(object):

    def __init__(self, port):
        # 初始化操作,创建属性
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # 创建TCP 套接字
        # 设置地址重用选项 解决由2MSL状态规定(30s到2min内主动断开TCP连接的一方
        # 不能立即绑定套接字端口
        # )的情况  ---> 理解重新绑定       套接字层面          重用地址选项    1设置 0取消
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 绑定 监听
        self.server_socket.bind(('', port))  # 需要以元组的形式传入
        self.server_socket.listen(128)  # 接受客户端连接最大个数

    def client_handler(self, client_socket):
        # 处理客户端的HTTP请求
        # 接受数据
        recv_data = client_socket.recv(4096)
        # 把字节类型数据转换成字符串类型数据,方便切割
        recv_str_data = recv_data.decode()

        # 请求报文中---》请求行[资源请求路径]
        # print(recv_data)
        data_set = recv_str_data.split("\r\n")
        # print(data_set)

        # 请求行
        request_line = data_set[0]
        print(request_line)
        # GET /index2.html HTTP/1.1
        result = re.match(r'\w+\s+(\S+)', request_line)

        if not result:
            print("HTTP请求报文格式错误")
            client_socket.close()
            return
        # 根据正则结果对象取出第一个分组--请求的资源路径
        path_info = result.group(1)
        print("接受到用户的资源请求 %s" % path_info)

        # 回数据
        # 根据用户替换的资源路径 读取本地的网页资源
        # /home/python/Desktop/1.jpg
        # static/index.html
        if path_info == '/':
            # 用户请求/获取意味着 获取网站首页  web服务器通用规则
            path_info = '/index.html'
        try:
            file = open("./static" + path_info, 'rb')
            file_data = file.read()  # 如果文件大 可能有隐患
            file.close()
        except Exception as e:
            # 响应行
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # 响应头
            response_header = "Server: PythonWebServer 5.0\r\n"
            response_body = "ERROR: file not found!!!"
            response_data = response_line + response_header+"\r\n"+response_body
            client_socket.send(response_data.encode())
        else:
            # 响应行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 响应头
            response_header = "Server: PythonWebServer 5.0\r\n"
            # 响应体-服务器存储给浏览器发送的资源文件数据
            response_body = file_data
            # 拼接HTTP响应报文数据
            response_data = (response_line+response_header+"\r\n").encode() + response_body
            client_socket.send(response_data)
        finally:
            # 关闭套接字
            client_socket.close()

    def start_threading(self):
        # 多线程启动
        while True:
            # 接受客户端请求
            client_socket, client_addr = self.server_socket.accept()
            print("接受到来自%s的链接请求" % str(client_addr))

            # 每接受一个客户端连接,创建一个线程,为客户服务
            thread = threading.Thread(target=self.client_handler, args=(client_socket,))
            # 运行线程
            thread.start()

    def start_processing(self):
        # 多进程启动
        while True:
            # 接受客户端请求
            client_socket, client_addr = self.server_socket.accept()
            print("接受到来自%s的链接请求" % str(client_addr))

            # 每接受到一个客户端连接 创建一个进程 为客户服务
            # 创建一个进程的执行计划
            process = multiprocessing.Process(target=self.client_handler, args=(client_socket,))
            # 创建并且运行线程
            process.start()

            # 由于父进程在创建子进程的时候,完全复制了父进程的系统资源
            # 将父进程中的client_socket资源释放
            client_socket.close()

    def start_gevent(self):
        # 协程版启动
        while True:
            # 接受客户端请求
            client_socket, client_addr = self.server_socket.accept()
            print("接受到来自%s的链接请求" % str(client_addr))

            # 每接受到一个客户端连接,创建一个协程并运行,为客户服务
            gevent.spawn(self.client_handler, client_socket)


def main():
    # 保存命令行参数的列表,每个元素都是字符串类型
    # print(sys.argv)
    # 第0个元素是程序名,第1个元素是参数--端口
    if len(sys.argv) < 2:
        print("参数错误: 请使用类似于 python web.py 8888")
        return
    port = int(sys.argv[1])

    http_server = HTTPServer(port)
    # 协程启动,启动时需要开启程序顶部的:monkey.patch_all()
    # http_server.start_gevent()
    # 多进程启动
    # http_server.start_processing()
    # 多线程启动
    http_server.start_threading()


if __name__ == "__main__":
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值