0
点赞
收藏
分享

微信扫一扫

python之静态服务器

橙子好吃吗 2022-01-12 阅读 91

目录

自带

1.单任务且只访问单个页面

2 单任务访问任意资源

3多任务版

4面向对象版

5终端版


自带

python -m http.server [port] 默认端口号8000

需在静态资源目录中运行

自搭

1.单任务且只访问单个页面

  1. 编写一个TCP服务端程序
  2. 获取浏览器发送的http请求报文数据
  3. 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。
  4. HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字。
import socket

if __name__=='__main__':
    server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    server_socket.bind(('',8080))
    server_socket.listen(128)
    while True:
        client_socket,client_port=server_socket.accept()
        # 这里是接收浏览器发送过来的请求报文
        client_data=client_socket.recv(4096).decode('utf8')
        print(client_data) #会打印2次 和js express一致 因为多了一次ico请求
        #GET /hfygjmvgjg HTTP/1.1 ...#GET /favicon.ico HTTP/1.1  ...
        # 浏览器请求完毕后读取指定的静态资源,然后发送给浏览器相应报文
        with open('static/index.html','r') as file:
            file_data=file.read()
        #响应行
        response_line='HTTP/1.1 200 OK\r\n'
        #响应头
        response_head='Server: PWS1.0\r\n'
        #响应空行 '\r\n'
        #响应体 就是静态资源代码
        response_body=file_data
        response_data=(response_line+response_head+'\r\n'+response_body).encode('utf8')
        client_socket.send(response_data)
        client_socket.close()

注意读取资源文件时候尽量使用rb 图片视频等特殊文件需二进制去读取

  1. 编写一个TCP服务端程序

    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 循环接受客户端的连接请求
    while True:
        conn_socket, ip_port = tcp_server_socket.accept()
    
  2. 获取浏览器发送的http请求报文数据

    client_request_data = conn_socket.recv(4096)
    
  3. 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。

    response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
    conn_socket.send(response_data)
    
  4. HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字。

    conn_socket.close()

2 单任务访问任意资源

该url指定是从浏览器请求中获取,获取到后对该url静态资源读取,存在响应 不存在响应404

  1. 获取用户请求资源的路径
  2. 根据请求资源的路径,读取指定文件的数据
  3. 组装指定文件数据的响应报文,发送给浏览器
  4. 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
import socket


def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    server_socket.bind(('', 9000))
    server_socket.listen(128)
    while True:
        newsocket, newsocket_port = server_socket.accept()
        requestData = newsocket.recv(4096)
        if len(requestData) == 0:
            print('浏览器关闭了')
            newsocket.close()
            return
        # 空格形式分隔2次获取路径  ['GET', '/', 'HTTP/1.1\r\nHost: 127.0.0.1:9000\r\nConnection: keep...
        request_path = requestData.decode('utf8').split(' ', 2)[1]
        print(request_path)  # /xxx  /favicon.ico //images/001.jpg /js/jquery-3.4.1.min.js读取代码文件内的路径也就是每个资源也会发送报文
        # 判断是否是根路径 是则指定index.html
        if request_path == '/':
            request_path = '/index.html'
        # 指定的路径不报错也就是不存在404,存在则读取后显示页面
        try:
            # 该资源文件内存在图片必须二进制文件读取 不然出现404
            with open('static' + request_path, 'rb') as file:
                file_data = file.read()
        except Exception as e:
            response_line = 'HTTP/1.1 404 Not Found\r\n'
            response_header = 'Server: PWS1.0\r\n'
            with open('static/error.html', 'rb') as file:
                file_data = file.read()
            response_body = file_data
            response_data = (response_line + response_header + '\r\n').encode('utf8') + response_body
            newsocket.send(response_data)
        else:
            response_line = 'HTTP/1.1 200 OK\r\n'
            response_header = 'Server: PWS1.0\r\n'
            response_body = file_data
            response_data = (response_line + response_header + '\r\n' ).encode('utf8')+response_body
            newsocket.send(response_data)
        finally:
            newsocket.close()


if __name__ == '__main__':
    main()

3多任务版

  1. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞。

  2. 把创建的子线程设置成为守护主线程,防止主线程无法退出。

import socket
import threading

def handle_client_request(client_scoket):
    recv_client_data=client_scoket.recv(4096)
    if len(recv_client_data)==0:
        print('浏览器关闭了')
        return
    request_path=recv_client_data.decode('utf8').split(' ',2)[1]
    if request_path=='/':
        request_path='/index.html'
    try:
        with open('static'+request_path,'rb') as file:
            file_data=file.read()
    except Exception as e:
        with open('static' + request_path, 'rb') as file:
            file_data = file.read()
        # 响应行
        response_line = "HTTP/1.1 404 Not Found\r\n"
        # 响应头
        response_header = "Server: PWS1.0\r\n"
        #响应空行 \r\n
        # 响应体
        response_body = file_data
        response_data=(response_line+response_header+'\r\n').encode('utf8')+response_body
        client_scoket.send(response_data)
    else:
        # 响应行
        response_line = "HTTP/1.1 200 OK\r\n"
        # 响应头
        response_header = "Server: PWS1.0\r\n"

        # 响应体
        response_body = file_data

        # 拼接响应报文
        response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
        # 发送数据
        client_scoket.send(response_data)
    finally:
        client_scoket.close()

def main():
    server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    server_socket.bind(('',9000))
    server_socket.listen(128)
    while True:
        client_socket,client_port=server_socket.accept()
        #连接连接后
        sub_thread=threading.Thread(target=handle_client_request,args=(client_socket,),daemon=True)
        sub_thread.start()
if __name__=='__main__':
    main()

4面向对象版

  1. 把提供服务的Web服务器抽象成一个类(HTTPWebServer)
  2. 提供Web服务器的初始化方法,在初始化方法里面创建socket对象
  3. 提供一个开启Web服务器的方法,让Web服务器处理客户端请求操作。
import socket
import threading


class HttpWebServer:
    def __init__(self):
        # 对socket对象以及监听 端口等初始化
        server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
        server_socket.bind(('',9000))
        server_socket.listen(128)
        self.server_socket=server_socket

    # 定义静态方法线程内程序
    @staticmethod
    def handle_client_request(new_socket):
        # 代码执行到此,说明连接建立成功
        recv_client_data = new_socket.recv(4096)
        if len(recv_client_data) == 0:
            print("关闭浏览器了")
            new_socket.close()
            return

        # 对二进制数据进行解码
        recv_client_content = recv_client_data.decode("utf-8")
        print(recv_client_content)
        # 根据指定字符串进行分割, 最大分割次数指定2
        request_list = recv_client_content.split(" ", maxsplit=2)

        # 获取请求资源路径
        request_path = request_list[1]
        print(request_path)

        # 判断请求的是否是根目录,如果条件成立,指定首页数据返回
        if request_path == "/":
            request_path = "/index.html"

        try:
            # 动态打开指定文件
            with open("static" + request_path, "rb") as file:
                # 读取文件数据
                file_data = file.read()
        except Exception as e:
            # 请求资源不存在,返回404数据
            # 响应行
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # 响应头
            response_header = "Server: PWS1.0\r\n"
            with open("static/error.html", "rb") as file:
                file_data = file.read()
            # 响应体
            response_body = file_data

            # 拼接响应报文
            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
            # 发送数据
            new_socket.send(response_data)
        else:
            # 响应行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 响应头
            response_header = "Server: PWS1.0\r\n"

            # 响应体
            response_body = file_data

            # 拼接响应报文
            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
            # 发送数据
            new_socket.send(response_data)
        finally:
            # 关闭服务与客户端的套接字
            new_socket.close()
    def start(self):
        while True:
            new_socket,ip_port=self.server_socket.accept()
            # 开启线程
            subthread=threading.Thread(target=self.handle_client_request,kwargs={'new_socket':new_socket},daemon=True)
            subthread.start()
def main():
    http_server=HttpWebServer()
    http_server.start()

if __name__=='__main__':
    main()

5终端版

  1. 获取执行python程序的终端命令行参数

     sys.argv   返回列表数据
    
  2. 判断参数的类型,设置端口号必须是整型是否是纯数字的字符串

     if not sys.argv[1].isdigit():
         print("启动命令如下: python3 xxx.py 9090")
         return
     port = int(sys.argv[1])
    
  3. 给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号

     def __init__(self, port):
         self.tcp_server_socket.bind((“”, port))
import socket
import sys
import threading


class HttpWebServer:
    def __init__(self,port):
        # 对socket对象以及监听 端口等初始化
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        server_socket.bind(('', port))
        server_socket.listen(128)
        self.server_socket = server_socket

    # 定义静态方法线程内程序
    @staticmethod
    def handle_client_request(new_socket):
        # 代码执行到此,说明连接建立成功
        recv_client_data = new_socket.recv(4096)
        if len(recv_client_data) == 0:
            print("关闭浏览器了")
            new_socket.close()
            return

        # 对二进制数据进行解码
        recv_client_content = recv_client_data.decode("utf-8")
        print(recv_client_content)
        # 根据指定字符串进行分割, 最大分割次数指定2
        request_list = recv_client_content.split(" ", maxsplit=2)

        # 获取请求资源路径
        request_path = request_list[1]
        print(request_path)

        # 判断请求的是否是根目录,如果条件成立,指定首页数据返回
        if request_path == "/":
            request_path = "/index.html"

        try:
            # 动态打开指定文件
            with open("static" + request_path, "rb") as file:
                # 读取文件数据
                file_data = file.read()
        except Exception as e:
            # 请求资源不存在,返回404数据
            # 响应行
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # 响应头
            response_header = "Server: PWS1.0\r\n"
            with open("static/error.html", "rb") as file:
                file_data = file.read()
            # 响应体
            response_body = file_data

            # 拼接响应报文
            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
            # 发送数据
            new_socket.send(response_data)
        else:
            # 响应行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 响应头
            response_header = "Server: PWS1.0\r\n"

            # 响应体
            response_body = file_data

            # 拼接响应报文
            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
            # 发送数据
            new_socket.send(response_data)
        finally:
            # 关闭服务与客户端的套接字
            new_socket.close()

    def start(self):
        while True:
            new_socket, ip_port = self.server_socket.accept()
            # 开启线程
            subthread = threading.Thread(target=self.handle_client_request, kwargs={'new_socket': new_socket},
                                         daemon=True)
            subthread.start()


def main():
    if len(sys.argv) != 2:
        print('执行命令如下: python3 xxx.py 8000')
        return
    # 判断字符串是否都是数字组成
    if not sys.argv[1].isdigit():
        print("执行命令如下: python3 xxx.py 8000,且端口号必须是数字")
        return
    port=int(sys.argv[1])

    http_server = HttpWebServer(port)
    http_server.start()


if __name__ == '__main__':
    main()

启动方式 终端中输入python  xxx.py 8000

举报

相关推荐

0 条评论