Python WEB learning notes - WEB server

Posted by deko on Fri, 11 Feb 2022 09:46:35 +0100

WEB server learning notes

1, Build a static web server

1. What is a static Web server?

A program that can provide static documents for the requesting browser. Usually, when we browse Baidu news, the news data will change every day. The page we visit is dynamic, while what we develop is static, and the data of the page will not change.

2. How to build Python's own static Web server?

Be sure to switch to the specified resource directory before executing the following command.
Build Python's own static Web server and use python3 -m http Server port number. The rendering is as follows:

-m identifies the module in the running package. When executing this command, you need to enter the directory of the static file you specify, and then you can access the corresponding html file through the browser. In this way, a static web server is built.

3. Access the built static server

Visit the built static web server through the browser, and the renderings are as follows:

You can see the request header, GET mode request, and the request path is the index under the current path HTML IP address is localhost (local address) port number: 9000

4. Summary

  • A static Web server is a program that provides static documents to the requesting browser
  • Build Python's own Web server and use python3 -m http The server port number command is sufficient. If the port number is not specified, it is 8000 by default

2, Static web server - return fixed page data

1. Develop your own static web server

Implementation steps:

  • Write a TCP server program
  • Obtain the http request message data sent by the browser
  • Read the fixed page data, assemble the page data into HTTP response message data and send it to the browser
  • After sending the HTTP response message data, close the socket serving the client

2. Static Web server - returns the instance code of fixed page data

import socket

if __name__ == '__main__':
    # Create a tcp server socket (this socket is only used to establish a connection)
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # IPv4 TCP protocol

    # Set the port number reuse, and the port number will be released immediately after the program exits
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)

    # Binding port number
    tcp_server_socket.bind(("",9000))

    # Set the maximum number of listeners
    tcp_server_socket.listen(128)

    while True:
        # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
        new_socket,ip_port = tcp_server_socket.accept()
        # The code execution here indicates that the connection is successful
        recv_data = new_socket.recv(4096)
        print(recv_data)

        with open("index.html","r",encoding="utf-8") as file:   # The file here represents the object to open the file
            file_data = file.read()  # Read data from file


        # Using with open means that the file closing operation is not completed by the programmer, but by the system


        # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol

        # Response line
        response_line = "HTTP/1.1 200 OK\r\n"
        # Response header
        response_header = "Server: PWS/1.0\r\n"
        # Empty line
        # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
        response_body = file_data

        response = response_line + response_header + "\r\n" + response_body

        new_socket.send(response.encode("utf-8"))  # Coding required: utf-8


        # Close the socket on the server
        new_socket.close()

3, Static server - returns the instance code of the specified page data

import socket

def H(self):
    # Create a tcp server socket (this socket is only used to establish a connection)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # IPv4 TCP protocol

    # Set the port number reuse, and the port number will be released immediately after the program exits
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # Binding port number
    tcp_server_socket.bind(("", 9000))

    # Set the maximum number of listeners
    tcp_server_socket.listen(128)

    while True:
        # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
        new_socket, ip_port = tcp_server_socket.accept()
        # The code execution here indicates that the connection is successful
        recv_data = new_socket.recv(4096)
        # print(recv_data)


        # If the accepted data length is 0, close the socket
        if len(recv_data) == 0:
            new_socket.close()
            return

            # First decode the binary data
        recv_content = recv_data.decode("'utf-8")
        print(recv_content)

        # Divide the data into spaces
        request_list = recv_content.split(" ", maxsplit=2)  # Split twice and return a tuple

        # Gets the path of the requested resource
        request_path = request_list[1]  # The second item of the tuple is the requested resource path

        print(request_path)

        # Judge whether the requested is the root directory. If it is the root directory, set the returned information
        if request_path == "/":
            request_path = "/index.html"

        with open("static" + request_path, "rb") as file:  # The file here represents the object to open the file
            file_data = file.read()  # Read data from file

        # Using with open means that the file closing operation is not completed by the programmer, but by the system

        # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol

        # Response line
        response_line = "HTTP/1.1 200 OK\r\n"
        # Response header
        response_header = "Server: PWS/1.0\r\n"
        # Empty line
        # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
        response_body = file_data

        response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

        new_socket.send(response)  # Coding required: utf-8

        # Close the socket on the server
        new_socket.close()


# Returns the specified page data
if __name__ == '__main__':
   H()

4, Static server - return to page 404

import socket

def H():
    # Create a tcp server socket (this socket is only used to establish a connection)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # IPv4 TCP protocol

    # Set the port number reuse, and the port number will be released immediately after the program exits
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # Binding port number
    tcp_server_socket.bind(("", 9000))

    # Set the maximum number of listeners
    tcp_server_socket.listen(128)

    while True:
        # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
        new_socket, ip_port = tcp_server_socket.accept()
        # The code execution here indicates that the connection is successful
        recv_data = new_socket.recv(4096)
        # print(recv_data)


        # If the accepted data length is 0, close the socket
        if len(recv_data) == 0:
            new_socket.close()
            return

            # First decode the binary data
        recv_content = recv_data.decode("'utf-8")
        print(recv_content)

        # Divide the data into spaces
        request_list = recv_content.split(" ", maxsplit=2)  # Split twice and return a tuple

        # Gets the path of the requested resource
        request_path = request_list[1]  # The second item of the tuple is the requested resource path

        # print(request_path)

        # Judge whether the requested is the root directory. If it is the root directory, set the returned information
        if request_path == "/":
            request_path = "/index.html"


        # Find out if the file path exists
        # 1. os.path.exits  os.path.exits("static/" + request_path)
        # 2. try-except


        try:
            with open("static" + request_path, "rb") as file:  # The file object here represents the open file
                file_data = file.read()  # Read data from file

        except Exception as e:
            # When the code is executed to this point, it indicates that the file is not requested to return 404 status information
            # Response line
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"

            # Read data from 404 pages
            with open("static/NotFound.html","rb") as file:
                file_data = file.read()

            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        else:  # There is this path to send data
            # Using with open means that the file closing operation is not completed by the programmer, but by the system

            # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol
            # Response line
            response_line = "HTTP/1.1 200 OK\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"
            # Empty line
            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        # Close the socket on the server
        new_socket.close()


# Returns the specified page data
if __name__ == '__main__':
   H()

5, Static Web server - multitasking

1. Problems with static Web server

The current web server can not support multi-user access at the same time, and can only process the requests of clients one by one. So how to develop a multi task web server to process the requests of multiple clients at the same time

Multithreading can be used to save memory resources more than processes

Implementation steps of multitasking web server program:

  • When the connection between the client and the server is established successfully, a sub thread is created, and the sub thread is used to process the request of the client to prevent the main thread from blocking
  • Set the created sub thread to guard the main thread to prevent the main thread from being unable to exit

Test code:

import socket
import threading

def Handle(new_socket):
    # Description of successful connection to code here
    recv_data = new_socket.recv(4096)
    # print(recv_data)

    # If the accepted data length is 0, close the socket
    if len(recv_data) == 0:
        new_socket.close()
        return

        # First decode the binary data
    recv_content = recv_data.decode("'utf-8")
    print(recv_content)

    # Divide the data into spaces
    request_list = recv_content.split(" ", maxsplit=2)  # Split twice and return a tuple

    # Gets the path of the requested resource
    request_path = request_list[1]  # The second item of the tuple is the requested resource path

    # print(request_path)

    # Judge whether the requested is the root directory. If it is the root directory, set the returned information
    if request_path == "/":
        request_path = "/index.html"

    # Find out if the file path exists
    # 1. os.path.exits  os.path.exits("static/" + request_path)
    # 2. try-except

    try:
        with open("static" + request_path, "rb") as file:  # The file object here represents the open file
            file_data = file.read()  # Read data from file

    except Exception as e:
        # When the code is executed to this point, it indicates that the file is not requested to return 404 status information
        # Response line
        response_line = "HTTP/1.1 404 Not Found\r\n"
        # Response header
        response_header = "Server: PWS/1.0\r\n"

        # Read data from 404 pages
        with open("static/NotFound.html", "rb") as file:
            file_data = file.read()

        # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
        response_body = file_data

        response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

        new_socket.send(response)  # Coding required: utf-8

    else:  # There is this path to send data
        # Using with open means that the file closing operation is not completed by the programmer, but by the system

        # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol
        # Response line
        response_line = "HTTP/1.1 200 OK\r\n"
        # Response header
        response_header = "Server: PWS/1.0\r\n"
        # Empty line
        # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
        response_body = file_data

        response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

        new_socket.send(response)  # Coding required: utf-8

    finally:
        # Close the socket on the server
        new_socket.close()


def H():
    # Create a tcp server socket (this socket is only used to establish a connection)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # IPv4 TCP protocol

    # Set the port number reuse, and the port number will be released immediately after the program exits
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # Binding port number
    tcp_server_socket.bind(("", 9000))

    # Set the maximum number of listeners
    tcp_server_socket.listen(128)

    while True:
        # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
        new_socket, ip_port = tcp_server_socket.accept()

        # After the connection is established, the main thread creates a child thread
        sub_thread = threading.Thread(target=Handle,args=(new_socket,))

        # Guard main thread
        sub_thread.setDaemon(True)

        # Start the child thread to execute the corresponding task
        sub_thread.start()

# Returns the specified page data
if __name__ == '__main__':
   H()

5, Static web server - object oriented development

1. Develop static Web server in object-oriented way

Implementation steps:

1. Abstract the Web server providing services into a class (HTTP webserver)
2. Provide the initialization method of Web server, and create socket object in the initialization method
3. Provide a method to start the web server and let the web server process the client request operation

2. Static Web server - Test code for object-oriented development

import socket
import threading


class HttpWebServer(object):

    # Initialize and create tcp server socket
    def __init__(self):
        # Create tcp server socket
        self.tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # ipv4 address Tcp transmission protocol
        # Set the port number reuse, and the port number will be released immediately after the program exits
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
        # Binding port number
        self.tcp_server_socket.bind(("",9000)) # The first parameter does not write anything, which represents the local ip address

        # Set the maximum number of listeners
        self.tcp_server_socket.listen(128)


    # Loop accept socket
    def start(self):
        while True:
            # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
            new_socket, ip_port = self.tcp_server_socket.accept()

            # After the connection is established, the main thread creates a sub thread and calls the static method to process the client request
            sub_thread = threading.Thread(target=self.Handle, args=(new_socket,))

            # Guard main thread
            sub_thread.setDaemon(True)

            # Start the child thread to execute the corresponding task
            sub_thread.start()


    # There is no self object in the method and the current class is not used to process the client request. It can be used as a static method
    @staticmethod
    def Handle(new_socket):
        # The code execution here indicates that the connection is successful
        recv_data = new_socket.recv(4096)
        # print(recv_data)

        # If the accepted data length is 0, close the socket
        if len(recv_data) == 0:
            new_socket.close()
            return

            # First decode the binary data
        recv_content = recv_data.decode("'utf-8")
        print(recv_content)

        # Divide the data into spaces
        request_list = recv_content.split(" ", maxsplit=2)  # Split twice and return a tuple

        # Gets the path of the requested resource
        request_path = request_list[1]  # The second item of the tuple is the requested resource path

        # print(request_path)

        # Judge whether the requested is the root directory. If it is the root directory, set the returned information
        if request_path == "/":
            request_path = "/index.html"

        # Find out if the file path exists
        # 1. os.path.exits  os.path.exits("static/" + request_path)
        # 2. try-except

        try:
            with open("static" + request_path, "rb") as file:  # The file object here represents the open file
                file_data = file.read()  # Read data from file

        except Exception as e:
            # When the code is executed to this point, it indicates that the file is not requested to return 404 status information
            # Response line
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"

            # Read data from 404 pages
            with open("static/NotFound.html", "rb") as file:
                file_data = file.read()

            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        else:  # There is this path to send data
            # Using with open means that the file closing operation is not completed by the programmer, but by the system

            # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol
            # Response line
            response_line = "HTTP/1.1 200 OK\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"
            # Empty line
            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        finally:
            # Close the socket on the server
            new_socket.close()



def main():
    # Create web server
    web_server = HttpWebServer()

    # Start server
    web_server.start()

# Returns the specified page data
if __name__ == '__main__':
   main()

3. Summary

  • Abstract the web server providing services into a class (HTTP webserver)

    class HttpWebServer(object):

  • Provide the initialization method of web server, and create socket object in the initialization method

    def __init__(self):
    # Initialize the server socket, set listening, and omit the code
    
    
  • Provide a method to start the Web server, and let the Web server handle the client request operation

    def start(self):
    
        while(True):
    
         # The server always accepts the client request and returns a tuple. The first parameter is the new socket and the second parameter is the client ip
         new_socket,ip_port = self.tcp_server_socket.accept()
    
         # After the connection is established, a sub thread is opened to process the client's request
         sub_thread = threading.Thread(target = self.Handle,args=(new_socket,))
    
         sub_thread.start()   # Open child thread 
         
    

6, Static WEB server - get terminal command line parameters

import sys

# Get the command line parameter of the terminal, and pass in the parameter list after the python interpreter
params = sys.argv  # z this is a list


print(params,type(params))

design sketch:

7, Static Web server -- command line startup and dynamic binding port number

1. Develop the command line and start the static web server with dynamic binding port number

Implementation steps:

  • Get the command line parameters of the terminal executing the python program
  • Judge the type of parameter and set the port number to be integer
  • Add a port number parameter to the initialization method of the Web server class to bind the port number

2. Static Web server -- example code of dynamic binding port number for command line startup

import socket
import threading
import sys


class HttpWebServer(object):

    # Initialize and create tcp server socket
    def __init__(self,port):
        # Create tcp server socket
        self.tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # ipv4 address Tcp transmission protocol
        # Set the port number reuse, and the port number will be released immediately after the program exits
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
        # Binding port number
        self.tcp_server_socket.bind(("",port)) # The first parameter does not write anything, which represents the local ip address

        # Set the maximum number of listeners
        self.tcp_server_socket.listen(128)


    # Loop accept socket
    def start(self):
        while True:
            # Wait to receive the request from the client and return a tuple. Unpack the tuple to get a new socket (for message exchange) and a client's ip address
            new_socket, ip_port = self.tcp_server_socket.accept()

            # After the connection is established, the main thread creates a sub thread and calls the static method to process the client request
            sub_thread = threading.Thread(target=self.Handle, args=(new_socket,))

            # Guard main thread
            sub_thread.setDaemon(True)

            # Start the child thread to execute the corresponding task
            sub_thread.start()


    # There is no self object in the method and the current class is not used to process the client request. It can be used as a static method
    @staticmethod
    def Handle(new_socket):
        # The code execution here indicates that the connection is successful
        recv_data = new_socket.recv(4096)
        # print(recv_data)

        # If the accepted data length is 0, close the socket
        if len(recv_data) == 0:
            new_socket.close()
            return

            # First decode the binary data
        recv_content = recv_data.decode("'utf-8")
        print(recv_content)

        # Divide the data into spaces
        request_list = recv_content.split(" ", maxsplit=2)  # Split twice and return a tuple

        # Gets the path of the requested resource
        request_path = request_list[1]  # The second item of the tuple is the requested resource path

        # print(request_path)

        # Judge whether the requested is the root directory. If it is the root directory, set the returned information
        if request_path == "/":
            request_path = "/index.html"

        # Find out if the file path exists
        # 1. os.path.exits  os.path.exits("static/" + request_path)
        # 2. try-except

        try:
            with open("static" + request_path, "rb") as file:  # The file object here represents the open file
                file_data = file.read()  # Read data from file

        except Exception as e:
            # When the code is executed to this point, it indicates that the file is not requested to return 404 status information
            # Response line
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"

            # Read data from 404 pages
            with open("static/NotFound.html", "rb") as file:
                file_data = file.read()

            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        else:  # There is this path to send data
            # Using with open means that the file closing operation is not completed by the programmer, but by the system

            # The client is a browser, so the data needs to be encapsulated into the data in the format of data response message of http protocol
            # Response line
            response_line = "HTTP/1.1 200 OK\r\n"
            # Response header
            response_header = "Server: PWS/1.0\r\n"
            # Empty line
            # The html file above the response body should be placed in the response body, which is the data that the browser really needs to parse and display
            response_body = file_data

            response = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            new_socket.send(response)  # Coding required: utf-8

        finally:
            # Close the socket on the server
            new_socket.close()



def main():

    # Get terminal command line parameters
    params = sys.argv

    if len(params) != 2:
        print("The format of the command executed is as follows: python3 xxx.py 9000")
        return

    # Judge whether the second parameter is a string composed of numbers
    if not params[1].isdigit():
        print("The format of the command executed is as follows: python3 xxx.py 9000")
        return

    # When the code is executed here, it indicates that the number of command line parameters must be two, and the second parameter is a string composed of numbers
    port = int(params[1])  # Command line parameters are all in string form


    # Create web server
    web_server = HttpWebServer(port)

    # Start server
    web_server.start()



# Returns the specified page data
if __name__ == '__main__':
   main()

Topics: Front-end Operation & Maintenance server