【python】如何自己开发 一个Web服务器

  • HTTP 请求报文格式:
Host: 127.0.0.1:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

image
- HTTP 响应报文格式:

HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 14 Mar 2018 09:52:48 GMT
Server: BWS/1.1

image

采用 TCP 协议创建 socket 套接字对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 复用端口
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定 IP 和端口
    server_socket.bind(('', 8888))
    # 开启监听,由主动模式变为被动模式
    server_socket.listen()
等待客户端的连接,并接收客户端请求数据
        # 等待客户端连接
        new_socket, client_addr = server_socket.accept()
        # 接收浏览器发送的请求
        recv_data = new_socket.recv(1024).decode()
        # 防止客户端下线导致bug
        if not recv_data:
            print("客户端下线!")
            new_socket.close()
            continue
解析请求并返回数据
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)
        if file_path == '/':
            file_path = '/myWeChat.html'
        try:
            # 采用二进制读取本地文件内容(方便读取图片等非文本文件)
            with open(f'.{file_path}', 'rb') as f:
                response_body = f.read()
        except Exception as reason:
            # 没有相关文件资源时返回 404 错误
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
            response_body = f'<h2>404 {reason}</h2>'.encode()
        else:
            # 解析请求
            response_line = "HTTP/1.1 200 OK\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
        finally:
            # 响应请求并返回数据
            response = response_line + response_headers
            new_socket.send(response.encode() + response_body)
            # 关闭
            new_socket.close()
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)

该代码可以显示指定页面

if file_path == '/':
    file_path = '/myWeChat.html'

该代码可以设置默认首页

完整代码:
import re
import socket


if __name__ == '__main__':
    """返回固定的页面数据"""
    # 采用 TCP 协议创建 socket 对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 使端口重复使用
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定 IP 和端口
    server_socket.bind(('', 8888))
    # 开启监听,由主动模式变为被动模式
    server_socket.listen()
    while True:
        # 等待客户端连接
        new_socket, client_addr = server_socket.accept()
        # 接收浏览器发送的请求
        recv_data = new_socket.recv(1024).decode()
        # 防止客户端下线导致bug
        if not recv_data:
            print("客户端下线!")
            new_socket.close()
            continue
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)
        if file_path == '/':
            file_path = '/myWeChat.html'
        try:
            # 采用二进制读取本地文件内容(方便读取图片等非文本文件)
            with open(f'.{file_path}', 'rb') as f:
                response_body = f.read()
        except Exception as reason:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
            response_body = f'<h2>404 {reason}</h2>'.encode()
        else:
            # 解析请求
            response_line = "HTTP/1.1 200 OK\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
        finally:
            # 响应请求并返回数据
            response = response_line + response_headers
            new_socket.send(response.encode() + response_body)
            # 关闭
            new_socket.close()

一个 web 服务器开发完了。等等,这个服务器好像有点问题,当一个客户端连接时其他客户端就没法连接了。我开发服务器并不是为一个人服务,开发服务器也要符合社会主义核心价值观,要为人民服务。

通过多协程实现多用户处理
import re
import socket
from gevent import monkey
import gevent
# 可以简单理解为将一些模块变为非阻塞,具体解释需要上网查阅资料
monkey.patch_all()


def handle_request(new_socket):
        # 接收浏览器发送的请求
        recv_data = new_socket.recv(1024).decode()
        # 防止客户端下线导致bug
        if not recv_data:
            print("客户端下线!")
            new_socket.close()
            return
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)
        if file_path == '/':
            file_path = '/myWeChat.html'
        try:
            # 采用二进制读取本地文件内容(方便读取图片等非文本文件)
            with open(f'.{file_path}', 'rb') as f:
                response_body = f.read()
        except Exception as reason:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
            response_body = f'<h2>404 {reason}</h2>'.encode()
        else:
            # 解析请求
            response_line = "HTTP/1.1 200 OK\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
        finally:
            # 响应请求并返回数据
            response = response_line + response_headers
            new_socket.send(response.encode() + response_body)
            # 关闭
            new_socket.close()
def main():
    """程序主入口,实现 web 服务器功能"""
    # 采用 TCP 协议创建 socket 对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 使端口重复使用
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定 IP 和端口
    server_socket.bind(('', 8888))
    # 开启监听,由主动模式变为被动模式
    server_socket.listen()
    while True:
        # 等待客户端连接
        new_socket, client_addr = server_socket.accept()
        gevent.spawn(handle_request, new_socket)

if __name__ == '__main__':
    main()

总感觉还是差了点什么,哦,对了,python 是面向对象语言,这个代码没有对象啊,现实中没有对象,写个代码还没有对象,一万点暴击伤害。下面来个终极版的面向对象封装

import re
import socket
from gevent import monkey
import gevent

monkey.patch_all()  # 可以简单理解为使一些模块变为非阻塞,具体原理可以上网查询资料


class WebServer:
    def __init__(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.server_socket.bind(('', 8888))
        self.server_socket.listen()

    def handle_request(self, new_socket):
        """处理请求并完成响应操作"""
        # 接收浏览器发送的请求
        recv_data = new_socket.recv(1024).decode()
        # 防止客户端下线导致bug
        if not recv_data:
            print("客户端下线!")
            new_socket.close()
            return
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)
        if file_path == '/':
            file_path = '/myWeChat.html'
        try:
            # 采用二进制读取本地文件内容(方便读取图片等非文本文件)
            with open(f'.{file_path}', 'rb') as f:
                response_body = f.read()
        except Exception as reason:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
            response_body = f'<h2>404 {reason}</h2>'.encode()
        else:
            # 解析请求
            response_line = "HTTP/1.1 200 OK\r\n"
            response_headers = "Server: WASPVAE/6.6\r\n"
            response_headers += "Connection: Keep_Alive\r\n"
            response_headers += "Content-Type: text/html;charset=utf-8\r\n"  # 解决中文乱码问题
            response_headers += "\r\n"
        finally:
            # 响应请求并返回数据
            response = response_line + response_headers
            new_socket.send(response.encode() + response_body)
            # 关闭
            new_socket.close()

    def run(self):
        """运行服务器,接收客户端的请求,处理请求并完成响应操作"""
        while True:
            # 等待客户端连接
            new_socket, client_addr = self.server_socket.accept()
            gevent.spawn(self.handle_request, new_socket)


def main():
    """程序主入口,实现 web 服务器功能"""
    web_server = WebServer()
    web_server.run()


if __name__ == '__main__':
    main()

可以在项目当前文件夹添加几个 html 文件在浏览器输入 localhost:8888/xxx.html 或者 127.0.0.1:8888/xxx.html测试下
有兴趣的朋友可以关注下我的微信个人订阅号python数据之路,里面有我之前自学 python 的一些资料和在黑马学习的心得与笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值