当前位置:   article > 正文

【python】如何自己开发 一个Web服务器_host: 192.168.9.125:8888 connection: keep-alive ca

host: 192.168.9.125:8888 connection: keep-alive cache-control: max-age=0 upg
  • 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
等待客户端的连接,并接收客户端请求数据
        # 等待客户端连接
        new_socket, client_addr = server_socket.accept()
        # 接收浏览器发送的请求
        recv_data = new_socket.recv(1024).decode()
        # 防止客户端下线导致bug
        if not recv_data:
            print("客户端下线!")
            new_socket.close()
            continue
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
解析请求并返回数据
        # 从请求行获取路径信息
        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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
        # 从请求行获取路径信息
        data_list = recv_data.splitlines()
        request_line = data_list[0]
        regex = re.match(r'.* (.*) .*', request_line)
        file_path = regex.group(1)
  • 1
  • 2
  • 3
  • 4
  • 5

该代码可以显示指定页面

if file_path == '/':
    file_path = '/myWeChat.html'
  • 1
  • 2

该代码可以设置默认首页

完整代码:
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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

一个 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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

总感觉还是差了点什么,哦,对了,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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/216974?site
推荐阅读
相关标签
  

闽ICP备14008679号