Web服务器开发 Day01

1.网址介绍

2.http协议介绍

超文本简而言之就是带有链接的文本数据 

浏览器访问Web服务器过程

 比如:

http的请求,响应报文格式如下: 

3.静态Web服务器搭建

3.1搭建python自带的服务器

3.2开发自己的静态web服务器之返回固定页面数据 

import socket

if __name__ == '__main__':
    # 1. 编写一个TCP服务端程序
    # 创建socket对象,使用IPv4地址族和TCP协议
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口复用选项,以便程序重启时可以快速重用端口
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定地址和端口,空字符串表示监听所有可用的本地IP地址,端口号为8080
    tcp_server_socket.bind(("", 8080))
    # 设置监听,最多允许128个连接进入等待队列
    tcp_server_socket.listen(128)

    while True:
        # 2. 获取浏览器发送的HTTP请求报文数据
        # 接受客户端的连接请求,返回一个新的套接字(用于和该客户端通信)和客户端的地址
        client_socket, client_addr = tcp_server_socket.accept()
        # 接收客户端发送的数据,最多接收1024字节,并解码为字符串
        client_request_data = client_socket.recv(1024).decode()
        print(client_request_data)

        # 3. 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
        try:
            # 以二进制只读模式打开本地的./static/index.html文件
            with open("./static/index.html", "rb") as f:
                file_data = f.read()
        except FileNotFoundError:
            # 如果文件不存在,设置相应的错误响应
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "File Not Found".encode()
        else:
            # 应答行,表明HTTP版本、状态码和状态描述
            response_line = "HTTP/1.1 200 OK\r\n"
            # 应答头,这里简单设置了服务器信息
            response_header = "Server:pwb\r\n"
            # 应答体,即读取到的页面文件内容
            response_body = file_data

        # 组装完整的HTTP响应数据,包括应答行、应答头(加上空行分隔)和应答体
        response_data = (response_line + response_header + "\r\n").encode() + response_body
        # 向客户端发送HTTP响应数据
        client_socket.send(response_data)

        # 4. HTTP响应报文数据发送完成以后,关闭服务于该客户端的套接字
        client_socket.close()

3.3开发自己的Web服务器之返回指定页面

以上代码无论客户端输入什么网址都会返回./static/index.html这一个固定的页面,如何返回用户指定的页面呢?

import socket

if __name__ == '__main__':
    # 创建 TCP 套接字,使用 IPv4 地址族和 TCP 协议
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口复用,以便程序重启时能快速重用端口
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定服务器监听的地址和端口,这里监听所有可用的本地 IP 地址,端口为 8080
    tcp_server_socket.bind(("", 8080))
    # 开始监听客户端连接,最大允许 128 个连接排队等待处理
    tcp_server_socket.listen(128)

    while True:
        # 接受客户端的连接请求,返回一个新的套接字用于和客户端通信,以及客户端的地址
        client_socket, client_addr = tcp_server_socket.accept()
        # 接收客户端发送的 HTTP 请求数据,最多接收 1024 字节,并将其解码为字符串
        client_request_data = client_socket.recv(1024).decode()
        print(client_request_data)

        # 将客户端请求数据按空格分割,得到请求的各个部分
        req_data = client_request_data.split(" ")
        # 获取请求的路径部分
        request_path = req_data[1]

        # 如果请求路径是根路径,将其设置为默认的 index.html 页面路径
        if request_path == "/":
            request_path = "/index.html"

        try:
            # 尝试以二进制只读模式打开请求路径对应的文件,文件位于 ./static 目录下
            with open("./static" + request_path, "rb") as f:
                file_data = f.read()
            # 如果文件读取成功,设置响应状态码为 200(表示成功)
            response_line = "HTTP/1.1 200 OK\r\n"
            # 设置响应头,指定服务器信息
            response_header = "Server:pwb\r\n"
            # 响应体为读取到的文件内容
            response_body = file_data
        except FileNotFoundError:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "404 Not Found".encode()
        except Exception as e:
            # 处理其他可能的异常,设置响应状态码为 500(表示服务器内部错误)
            response_line = "HTTP/1.1 500 Internal Server Error\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "Internal Server Error".encode()

        # 组装完整的 HTTP 响应数据,包括响应行、响应头(加上空行分隔)和响应体
        response_data = (response_line + response_header + "\r\n").encode() + response_body
        # 向客户端发送 HTTP 响应数据
        client_socket.send(response_data)
        # 关闭与客户端的连接,释放资源
        client_socket.close()

3.4 静态Web服务器多线程版本

import socket
import threading


# 处理客户端请求的函数
def handle_client_request(client_socket):
    try:
        # 接收客户端发送的HTTP请求数据,最多接收1024字节,并解码为字符串
        client_request_data = client_socket.recv(1024).decode()
        print(client_request_data)

        # 将客户端请求数据按空格分割,得到请求的各个部分
        req_data = client_request_data.split(" ")
        # 获取请求的路径部分
        request_path = req_data[1]

        # 如果请求路径是根路径,将其设置为默认的index.html页面路径
        if request_path == "/":
            request_path = "/index.html"

        try:
            # 尝试以二进制只读模式打开请求路径对应的文件,文件位于./static目录下
            with open("./static" + request_path, "rb") as f:
                file_data = f.read()
            # 如果文件读取成功,设置响应状态码为200(表示成功)
            response_line = "HTTP/1.1 200 OK\r\n"
            # 设置响应头,指定服务器信息
            response_header = "Server:pwb\r\n"
            # 响应体为读取到的文件内容
            response_body = file_data
        except FileNotFoundError:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "404 Not Found".encode()
        except Exception as e:
            # 处理其他可能的异常,设置响应状态码为500(表示服务器内部错误)
            response_line = "HTTP/1.1 500 Internal Server Error\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "Internal Server Error".encode()

        # 组装完整的HTTP响应数据,包括响应行、响应头(加上空行分隔)和响应体
        response_data = (response_line + response_header + "\r\n").encode() + response_body
        # 向客户端发送HTTP响应数据
        client_socket.send(response_data)
    except Exception as e:
        print(f"处理客户端请求时发生异常: {e}")
    finally:
        # 关闭与客户端的连接,释放资源
        client_socket.close()


if __name__ == '__main__':
    # 创建TCP套接字,使用IPv4地址族和TCP协议
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口复用,以便程序重启时能快速重用端口
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定服务器监听的地址和端口,这里监听所有可用的本地IP地址,端口为8080
    tcp_server_socket.bind(("", 8080))
    # 开始监听客户端连接,最大允许128个连接排队等待处理
    tcp_server_socket.listen(128)
    print("服务器已启动,正在监听8080端口...")

    while True:
        # 接受客户端的连接请求,返回一个新的套接字用于和客户端通信,以及客户端的地址
        client_socket, client_addr = tcp_server_socket.accept()
        print(f"接收到来自 {client_addr} 的连接请求")
        # 创建子线程来处理客户端请求
        sub_thread = threading.Thread(target=handle_client_request, args=(client_socket,))
        sub_thread.start()

但是当客户端关闭时,会返回一个" " ,这时request_path = req_data[1]会出现错误,这时就需要改进函数,增加判断内容。即如果客户关闭了客户端这个进程会自动退出(函数被return了)

3.5面向对象开发Web服务器

import socket
import threading


class HttpWebServer:
    def __init__(self):
        # 1. 编写一个TCP服务端程序
        # 创建socekt
        self.tcp_server_socekt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口复用
        self.tcp_server_socekt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址
        self.tcp_server_socekt.bind(("", 8080))
        # 设置监听
        self.tcp_server_socekt.listen(128)

    def handle_client_request(self, client_socekt):
        client_request_data = client_socekt.recv(1024).decode()
        print(client_request_data)
        # 获取用户请求资源的路径
        requst_data = client_request_data.split(" ")
        print(requst_data)
        # 判断客户端是否关闭
        if len(requst_data) == 1:
            client_socekt.close()
            return
        # 求资源的路径
        request_path = requst_data[1]
        if request_path == "/":
            request_path = "/index.html"

        # 3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
        # 根据请求资源的路径,读取指定文件的数据
        try:
            with open("./static" + request_path, "rb") as f:
                file_data = f.read()
        except Exception as e:
            # 返回404错误数据
            # 应答行
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # 应答头
            response_header = "Server:pwb\r\n"
            # 应答体
            response_body = "404 Not Found sorry"
            # 应答数据
            # 组装指定文件数据的响应报文,发送给浏览器
            response_data = (response_line + response_header + "\r\n" + response_body).encode()

            client_socekt.send(response_data)
        else:
            # 应答行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 应答头
            response_header = "Server:pwb\r\n"
            # 应答体
            response_body = file_data
            # 应答数据
            # 组装指定文件数据的响应报文,发送给浏览器
            response_data = (response_line + response_header + "\r\n").encode() + response_body

            client_socekt.send(response_data)
        finally:
            # 4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
            client_socekt.close()

    def start(self):
        while True:
            # 2.获取浏览器发送的HTTP请求报文数据
            # 建立链接
            client_socekt, client_addr = self.tcp_server_socekt.accept()
            # 创建子线程
            sub_thread = threading.Thread(target=self.handle_client_request, args=(client_socekt,))
            sub_thread.start()


if __name__ == '__main__':
    # 创建服务器对象
    my_web_server = HttpWebServer()
    # 启动服务器
    my_web_server.start()

3.6动态绑定端口号

发现以上代码都是固定端口号是8080,那怎么样动态的规定服务器的端口号呢

 先介绍一下获取终端命令的方法:下图最后一行的sys模块里的argv可以用空行切割命令并按列表返回。       

 但可能终端输入的格式有问题所以要识别一下

但是return要在函数里,我们可以把这个代码放在main函数里并调用它。 

接下来继续完成main函数就行了,并且在类init方法里改就行了:

完整代码

import socket
import threading
import sys

class HttpWebServer:
    def __init__(self, port):
        # 1. 编写一个 TCP 服务端程序
        # 创建 socket
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口复用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址和动态传入的端口
        self.tcp_server_socket.bind(("", port))
        # 设置监听
        self.tcp_server_socket.listen(128)

    def handle_client_request(self, client_socket):
        try:
            client_request_data = client_socket.recv(1024).decode()
            print(client_request_data)
            # 获取用户请求资源的路径
            request_data = client_request_data.split(" ")
            print(request_data)
            # 判断客户端是否关闭
            if len(request_data) == 1:
                client_socket.close()
                return
            # 获取请求资源的路径
            request_path = request_data[1]
            if request_path == "/":
                request_path = "/index.html"

            # 3. 读取固定页面数据,把页面数据组装成 HTTP 响应报文数据发送给浏览器
            # 根据请求资源的路径,读取指定文件的数据
            try:
                with open("./static" + request_path, "rb") as f:
                    file_data = f.read()
            except Exception as e:
                # 返回 404 错误数据
                # 应答行
                response_line = "HTTP/1.1 404 Not Found\r\n"
                # 应答头
                response_header = "Server:pwb\r\n"
                # 应答体
                response_body = "404 Not Found sorry"
                # 应答数据
                # 组装指定文件数据的响应报文,发送给浏览器
                response_data = (response_line + response_header + "\r\n" + response_body).encode()
                client_socket.send(response_data)
            else:
                # 应答行
                response_line = "HTTP/1.1 200 OK\r\n"
                # 应答头
                response_header = "Server:pwb\r\n"
                # 应答体
                response_body = file_data
                # 应答数据
                # 组装指定文件数据的响应报文,发送给浏览器
                response_data = (response_line + response_header + "\r\n").encode() + response_body
                client_socket.send(response_data)
        finally:
            # 4. HTTP 响应报文数据发送完成以后,关闭服务于客户端的套接字
            client_socket.close()

    def start(self):
        while True:
            # 2. 获取浏览器发送的 HTTP 请求报文数据
            # 建立链接
            client_socket, client_addr = self.tcp_server_socket.accept()
            # 创建子线程
            sub_thread = threading.Thread(target=self.handle_client_request, args=(client_socket,))
            sub_thread.start()

if __name__ == '__main__':
    # 默认端口号
    default_port = 8080
    if len(sys.argv) > 1:
        try:
            # 尝试从命令行参数获取端口号
            port = int(sys.argv[1])
        except ValueError:
            print("输入的端口号不是有效的整数,将使用默认端口 8080。")
            port = default_port
    else:
        port = default_port

    # 创建服务器对象
    my_web_server = HttpWebServer(port)
    print(f"服务器已启动,监听端口 {port}...")
    # 启动服务器
    my_web_server.start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值