Start with a Hello World program
To write a Web server, you need a Python built-in library called Socket. Socket is a relatively abstract concept, Chinese called a Socket, it represents a network connection. To communicate between two computers, there are roughly three steps: establish a connection, transfer data, and close the connection. The Socket library provides this capability.
Following international convention, we will begin our Web server learning by writing a Hello World program.
Create a TCP-based socket object:
# import socket
import socket
Create a socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Copy the code
The socket.socket() method is used to create a socket object. We also pass it two arguments: socket.AF_INET to indicate that IPv4 is used, and socket.SOCK_STREAM to indicate that this is a TCP-based socket object. These two parameters are also default parameters and can be passed without.
The HTTP protocol is based on the request-response model, in which requests can only be initiated by the client and responded by the server. The server does not have the ability to initiate requests actively, but it has to wait passively for requests from the client. So now that we have the socket object, all we need to do is listen for client requests:
# Bind IP and port
sock.bind(('127.0.0.1'.8000))
# Start listening
sock.listen(5)
Copy the code
The socket object’s bind method is used to bind the listening IP address and port. It accepts a tuple of IP and port, 127.0.0.1 representing the local IP address that only browsers running on the local machine can connect to. The port number ranges from 0 to 65535, but the port number smaller than 1024 requires administrator rights. Sock.listen (5) is used to enable listening. The maximum number of connections to wait is specified as 5.
After listening is enabled, you can wait to receive requests from clients:
client, addr = sock.accept()
Copy the code
Sock.accept () blocks the program, waiting for a client to connect, and returns the client connection object and the client address, respectively, once a client is connected.
After establishing a connection with the client, the next step is to receive the request data from the client:
data = b''
while True:
chunk = client.recv(1024)
data += chunk
if len(chunk) < 1024:
break
Copy the code
Receiving client request data requires calling the recV method of the client connection object with the length of data received each time. Data in socket communication is Python bytes. It receives 1024 bytes at a time and exits the loop until all data is received.
After receiving the data from the client, it needs to process the data and return a response to the client’s browser:
Print the data received from the client
print(f'data: {data}')
Send response data to the client
client.sendall(B 'HTTP/1.1 200 OK\r\ nContent-type: text/ HTML \r\n\r\nHello World
)
Copy the code
For the sake of simplicity, it prints the data directly after receiving it from the client without further parsing. The server then sends the response data to the client. The sent data is also bytes. The data is assembled according to the specification of the HTTP protocol, starting with the status line HTTP/1.1 200 OK, followed by a newline \r\n, followed by the response header Content-Type: Text/HTML Specifies that the response result is of HTML type, followed by two consecutive \r\n\r\n, and the body of the response
Hello World
.
After sending the response data, we need to close the client connection object and the server socket object:
Close the client connection object
client.close()
Close the socket object
sock.close()
Copy the code
At this point, a Hello World server program is written, and here is the complete code:
# server.py
import socket
def main() :
Create a socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Allow port reuse
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind IP and port
sock.bind(('127.0.0.1'.8000))
# Start listening
sock.listen(5)
Wait for the client request
client, addr = sock.accept()
print(f'client type: {type(client)}\naddr: {addr}')
Receive data from the client
data = b''
while True:
chunk = client.recv(1024)
data += chunk
if len(chunk) < 1024:
break
Print the data received from the client
print(f'data: {data}')
Send response data to the client
client.sendall(B 'HTTP/1.1 200 OK\r\ nContent-type: text/ HTML \r\n\r\nHello World
)
Close the client connection object
client.close()
Close the socket object
sock.close()
if __name__ == '__main__':
main()
Copy the code
Write the above code to the server.py file. Then run the file in Python on the terminal: python3 server.py.
Open your browser, type http://127.0.0.1:8000 in the address bar, and you’ll get the following results:
Hello World! The browser successfully rendered the server’s response.
Back to the terminal, you can view the printed client request information:
You can see that the client connection object is actually a socket object, and the client IP address is 127.0.0.1 and the port is 50510. Finally, the client requests the data, only the request line and the request header, and since there is no request body, it ends with two consecutive \r\n\r\n.
Careful readers may have noticed that in the complete Hello World program code presented at the end, after creating the socket object, there is a line:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Copy the code
What this line of code does is not explained earlier, but what it does is allow port reuse. If you do not write this line of code, when you need to restart the program immediately after the program runs, the program will throw an exception because the port is still occupied last time, and the port will be released for use after a period of time. Add this line of code to eliminate this problem and facilitate debugging.
Above, we have implemented a simple server program that can return Hello World.
Keep the server running forever
The Hello World server program implemented above runs once and exits. Typically, server-side programs are programs that run forever. Because you don’t know when the client is sending a request, you need the server to always be listening. This ensures that any request sent by the client can be received by the server.
# server_forever.py
import socket
def main() :
Create a socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Allow port reuse
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind IP and port
sock.bind(('127.0.0.1'.8000))
# Start listening
sock.listen(5)
while True:
Wait for the client request
client, addr = sock.accept()
print(f'client type: {type(client)}\naddr: {addr}')
Receive data from the client
data = b''
while True:
chunk = client.recv(1024)
data += chunk
if len(chunk) < 1024:
break
Print the data received from the client
print(f'data: {data}')
Send response data to the client
client.sendall(B 'HTTP/1.1 200 OK\r\ nContent-type: text/ HTML \r\n\r\nHello World
)
Close the client connection object
client.close()
if __name__ == '__main__':
main()
Copy the code
The above application adds a while True infinite loop. After processing a client connection object, the application immediately executes to the next loop and starts waiting for a new client connection, thus implementing the server application permanently. The last line of sock.close() in the main function was removed, since there was no need to close the server socket connection to keep the program running permanently.
Save the above code to the server_forever.py file and run the program from the command line using Python. The browser will refresh the page several times and still load Hello World.
However, if you look at the print on the terminal at this point, you will see that instead of just one request at a time, the browser will send two requests each time it refreshes the browser.
Open the Chrome console to view Network, and sure enough, the browser sends two requests.
The first request path is /, which is expected based on the browser request and response history.
The second request path is /favicon.ico, and the response to this request is also
Hello World
.
In fact, this request is made by The Chrome browser to get the website icon. When the homepage of JINGdong website is opened in the browser, the browser TAB bar will load the icon of jingdong website.
Our own Hello World server didn’t return the correct icon file. Instead, it returned a < H1 >Hello World string, so the browser didn’t recognize it as an icon. Eventually, there will be no jingdong icon in the Hello World TAB bar. This is not a problem we need to worry about for now and will be solved later when we implement the Todo List program.
Some readers may wonder why the Hello World server returns an incomplete HTML page, just a string < H1 >Hello World
with an H1 tag, and the browser renders the page and boldes Hello World. This is actually Chrome’s fault-tolerant mechanism. If it detects an incomplete HTML tag, it will automatically complete the missing tag. To achieve better rendering effect.
Now to end the server program, just press Ctrl + C on the terminal where the program is running.
Let the server support multiple client connections simultaneously
The Hello World server program we are implementing is single-threaded, so the server can only handle one request at a time. However, jingdong and other websites we use actually have many clients connected at the same time. If only one request can be processed at a time, the client experience will be very poor.
In order for our program to also support handling multiple client connections at the same time, we need to change it to a multithreaded version.
# threading_server_forever.py
import socket
import threading
def process_connection(client) :
""" Handling client connections """
Receive data from the client
data = b''
while True:
chunk = client.recv(1024)
data += chunk
if len(chunk) < 1024:
break
Print the data received from the client
print(f'data: {data}')
Send response data to the client
client.sendall(B 'HTTP/1.1 200 OK\r\ nContent-type: text/ HTML \r\n\r\nHello World
)
Close the client connection object
client.close()
def main() :
Create a socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Allow port reuse
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind IP and port
sock.bind(('127.0.0.1'.8000))
# Start listening
sock.listen(5)
while True:
Wait for the client request
client, addr = sock.accept()
print(f'client type: {type(client)}\naddr: {addr}')
Create a new thread to handle client connections
t = threading.Thread(target=process_connection, args=(client,))
t.start()
if __name__ == '__main__':
main()
Copy the code
With the multithreaded version, every time the server receives a client connection, it hands it off to a new child thread, and the main thread continues on to the next loop waiting for a new client connection. This enables the server to support multiple client connections simultaneously.
This chapter learned about Web server development by writing a Hello World program. If you’re new to programming and still have a bit of trouble understanding socket programming, you can compare it to Python’s file manipulation. File processing usually involves three steps: opening the file, reading and writing data, and closing the file. It’s also a good way to use existing knowledge as an analogy for learning a new technology.
This chapter source: chapter2
Contact me:
-
WeChat: jianghushinian
-
Email address: [email protected]
-
Jianghushini.cn