0


计算机网络第二章:作业 1: Web 服务器

文档:Web服务器的实现和测试

一、问题描述

本次实验要求开发一个简单的基于Python的Web服务器,服务器能够处理HTTP请求并返回HTML文件的内容。具体来说,Web服务器需要执行以下操作:

  1. 接收并解析HTTP请求:Web服务器从客户端接收HTTP请求,并解析所请求的文件。
  2. 从文件系统中读取文件:根据客户端请求,服务器从文件系统中获取相应的文件内容。
  3. 发送HTTP响应:服务器将文件内容封装到HTTP响应中并返回给客户端。
  4. 错误处理:如果请求的文件不存在,服务器返回一个“404 Not Found”错误页面。
  5. 测试服务器:使用浏览器访问服务器,验证HTML文件内容的返回及404错误处理。

二、问题解决方案

1. 开发环境
  • 编程语言:Python
  • 使用的库socket 模块用于TCP连接和套接字编程。
2. 设计思路
  1. 创建服务器套接字:在特定的IP地址和端口上监听客户端的TCP连接请求。
  2. 解析HTTP请求:当接收到来自客户端的请求时,解析请求报文中的文件名。
  3. 文件读取与响应构建:读取服务器上的HTML文件,并返回HTTP 200响应。如果文件不存在,返回404错误页面。
  4. 关闭连接:每次处理完请求后,关闭与客户端的连接。
3. 代码实现
from socket import*# 创建服务器套接字
serverSocket = socket(AF_INET, SOCK_STREAM)# 准备服务器,绑定端口 6789 并开始监听连接
serverSocket.bind(('',6789))
serverSocket.listen(1)print('Server is ready to receive...')whileTrue:# 接受来自客户端的连接
    connectionSocket, addr = serverSocket.accept()print(f'Connection established with {addr}')try:# 从客户端接收HTTP请求
        message = connectionSocket.recv(1024).decode()# 提取请求的文件名
        filename = message.split()[1]# 获取请求的文件路径print(f'Requested file: {filename}')# 打开并读取该文件内容withopen(filename[1:],'r')as f:
            outputdata = f.read()# 发送HTTP响应头
        connectionSocket.send("HTTP/1.1 200 OK\r\n\r\n".encode())# 发送文件内容作为响应体for i inrange(0,len(outputdata)):
            connectionSocket.send(outputdata[i].encode())# 关闭连接
        connectionSocket.close()except IOError:# 如果文件不存在,返回404错误
        error_message ="<html><body><h1>404 Not Found</h1></body></html>"
        connectionSocket.send("HTTP/1.1 404 Not Found\r\n\r\n".encode())
        connectionSocket.send(error_message.encode())# 关闭连接
        connectionSocket.close()# 关闭服务器(实际上,这在代码中永远不会到达)
serverSocket.close()
4. 代码说明
  1. 导入socket模块:使用from socket import *导入所需的套接字模块。
  2. 创建服务器套接字:调用socket()函数创建TCP套接字,使用bind()将服务器绑定到指定的端口号,使用listen()函数使服务器准备好接受连接。
  3. 处理客户端请求:使用accept()函数接受客户端的连接。recv()函数用于从客户端接收HTTP请求报文,并通过split()解析报文获取文件路径。
  4. 读取文件并响应:根据请求的文件路径打开文件,读取其内容,并使用send()将响应返回给客户端。如果请求的文件不存在,服务器返回404错误页面。
  5. 关闭连接:处理完成后,使用close()关闭与客户端的连接。
5. 错误处理

在代码中,使用

try-except

块来处理文件读取过程中的异常。文件不存在时,服务器会返回HTTP 404错误。

三、测试服务器

1. 准备工作
  • 在服务器所在的目录中创建一个HTML文件(如HelloWorld.html),其内容可以是简单的HTML代码,如:
<!DOCTYPEhtml><html><head><title>Hello World</title></head><body><h1>Hello, World!</h1></body></html>
  • 运行服务器程序:
python webserver.py
2. 测试步骤
  1. 获取服务器的IP地址 使用ifconfig命令获取服务器的IP地址。比如在wlo1接口下,IP地址是172.20.10.2
  2. 在浏览器中访问服务器- 打开浏览器,输入http://172.20.10.2:6789/HelloWorld.html。如果服务器运行正常,浏览器将显示HelloWorld.html的内容,即“Hello, World!”。- 如果尝试访问一个不存在的文件,如http://172.20.10.2:6789/nonexistent.html,则服务器将返回404 Not Found错误页面。
3. 预期结果

四、总结

通过本次实验,我们实现了一个简单的Python Web服务器,并学会了如何使用套接字来处理HTTP请求。我们完成了以下任务:

  • 构建了Web服务器处理HTTP请求。
  • 实现了文件读取及HTTP响应功能。
  • 处理了404错误的情况。
  • 使用浏览器测试了服务器的功能,并成功获取文件内容以及处理不存在的文件。

五、注意事项

  • 确保防火墙没有阻止端口6789的外部访问。
  • 服务器和客户端应处于同一局域网中,或者服务器的IP地址是公开的。
  • 在不同的设备或不同的网络环境中,确保服务器IP地址和端口号正确配置。

可选练习1

要实现一个能够同时处理多个请求的多线程Web服务器,我们可以使用Python的

threading

模块。在这个实现中,服务器主线程会监听新的连接,每当收到一个客户端连接请求时,它会启动一个新的线程来处理该请求,从而允许服务器同时处理多个请求。

下面是一个使用多线程的Web服务器的完整实现代码。

多线程Web服务器代码:

#import socket modulefrom socket import*import threading

# Define the thread function to handle client requestsdefhandle_client(connectionSocket):try:# Receive the request message from the client
        message = connectionSocket.recv(1024)# Extract the file name from the HTTP request
        filename = message.split()[1]# Open the file requested by the client
        f =open(filename[1:])# Read the file content
        outputdata = f.read()# Send HTTP header (response code 200 OK) to the client
        connectionSocket.send("HTTP/1.1 200 OK\r\n\r\n".encode())# Send the content of the requested file to the clientfor i inrange(0,len(outputdata)):
            connectionSocket.send(outputdata[i].encode())# Close the client socket
        connectionSocket.close()except IOError:# Send response message for file not found (404 Not Found)
        connectionSocket.send("HTTP/1.1 404 Not Found\r\n\r\n".encode())
        connectionSocket.send("<html><head></head><body><h1>404 Not Found</h1></body></html>".encode())# Close the client socket
        connectionSocket.close()# Main function to start the server and listen for incoming connectionsdefstart_server():# Prepare a server socket
    serverSocket = socket(AF_INET, SOCK_STREAM)# Bind the server to localhost at port 6789 (or any available port)
    serverSocket.bind(('',6789))# Listen for incoming connections, set the backlog queue size to 5
    serverSocket.listen(5)print("The server is ready to receive on port 6789...")whileTrue:# Establish the connection with the client
        connectionSocket, addr = serverSocket.accept()# Print the connection details (useful for debugging)print(f"Connection from {addr}")# Create a new thread for each client request
        client_thread = threading.Thread(target=handle_client, args=(connectionSocket,))# Start the thread
        client_thread.start()# Start the serverif __name__ =="__main__":
    start_server()

代码解释:

  1. 线程处理函数 (handle_client): 这是一个独立的函数,用于处理客户端的HTTP请求。它接收客户端的请求,解析文件名,读取文件并将其内容返回给客户端。如果文件不存在,则返回“404 Not Found”错误。defhandle_client(connectionSocket):# Process client request here每次有新的客户端连接时,都会创建一个新的线程来运行这个函数。
  2. 多线程实现: 使用Python的 threading.Thread 创建新的线程。每当有新的客户端连接到服务器时,主服务器线程都会创建一个新的线程来处理该连接。这允许多个客户端同时连接到服务器,而服务器不会因为处理某个请求而阻塞。client_thread = threading.Thread(target=handle_client, args=(connectionSocket,))client_thread.start()
  3. 服务器主线程 (start_server): 服务器主线程负责监听来自客户端的连接请求。每当接收到一个新的连接时,它会接受连接并创建一个新的线程来处理这个连接。serverSocket = socket(AF_INET, SOCK_STREAM)serverSocket.bind(('',6789))serverSocket.listen(5)``````listen(5) 表示服务器可以同时保持最多5个等待连接的客户端请求。
  4. 主程序 (if __name__ == "__main__"): 程序入口,通过 start_server() 启动服务器。

验证步骤:

  1. 将服务器代码和HTML文件放置在同一目录下。
  2. 启动服务器。
  3. 打开多个浏览器窗口或使用多台设备,访问服务器的URL(例如 http://localhost:6789/HelloWorld.html),确保服务器可以同时处理多个请求。
  4. 在访问不存在的文件时,验证是否收到404错误消息。

多线程的优势:

使用多线程可以使服务器同时处理多个请求,而不是一次只处理一个请求。这种设计可以显著提高服务器的并发能力,特别是在处理多个客户端连接时。

可选练习2

为了编写一个简单的HTTP客户端,我们可以使用Python的

socket

模块进行TCP连接,并向服务器发送HTTP请求。客户端需要通过命令行参数获取服务器的IP地址或主机名、端口号以及请求的文件路径。接着,它会通过TCP连接发送一个GET请求,并将服务器的响应内容打印出来。

以下是HTTP客户端的完整代码:

client.py

客户端代码:

import sys
from socket import*# Ensure that the correct number of arguments are providediflen(sys.argv)!=4:print("Usage: python client.py server_host server_port filename")
    sys.exit()# Get the server host, port, and filename from command-line arguments
server_host = sys.argv[1]
server_port =int(sys.argv[2])
filename = sys.argv[3]# Create a TCP socket
clientSocket = socket(AF_INET, SOCK_STREAM)# Try to connect to the servertry:
    clientSocket.connect((server_host, server_port))except Exception as e:print(f"Failed to connect to {server_host}:{server_port}")print(e)
    sys.exit()# Form the HTTP GET request
request_line =f"GET /{filename} HTTP/1.1\r\n"
host_header =f"Host: {server_host}:{server_port}\r\n"
connection_header ="Connection: close\r\n\r\n"

http_request = request_line + host_header + connection_header

# Send the HTTP GET request to the server
clientSocket.send(http_request.encode())# Receive the response from the server
response =b""whileTrue:
    data = clientSocket.recv(1024)ifnot data:break
    response += data

# Decode and print the response from the serverprint(response.decode())# Close the socket
clientSocket.close()

代码解释:

  1. 获取命令行参数:iflen(sys.argv)!=4:print("Usage: python client.py server_host server_port filename") sys.exit()这里首先检查命令行参数的数量是否正确,要求用户提供三个参数:服务器的主机名或IP地址,服务器端口,以及要请求的文件路径。如果参数数量不正确,程序会显示用法并退出。
  2. 解析命令行参数:server_host = sys.argv[1]server_port =int(sys.argv[2])filename = sys.argv[3]分别将服务器主机、端口和文件路径从命令行参数中读取。
  3. 创建TCP套接字并连接到服务器:clientSocket = socket(AF_INET, SOCK_STREAM)clientSocket.connect((server_host, server_port))创建一个TCP套接字,并尝试与服务器建立连接。如果连接失败,会捕获异常并退出程序。
  4. 生成并发送HTTP GET请求:request_line =f"GET /{filename} HTTP/1.1\r\n"host_header =f"Host: {server_host}:{server_port}\r\n"connection_header ="Connection: close\r\n\r\n"http_request = request_line + host_header + connection_headerclientSocket.send(http_request.encode())根据HTTP协议构建GET请求,包含请求行、主机头(Host),以及表示连接关闭的头部。构建好请求后,将其编码成字节并通过套接字发送给服务器。
  5. 接收并显示服务器的响应:response =b""whileTrue: data = clientSocket.recv(1024)ifnot data:break response += dataprint(response.decode())客户端通过循环调用 recv() 方法,以1024字节为单位接收服务器响应的数据。直到服务器关闭连接并不再发送数据。最后,将接收到的字节数据解码为字符串并打印出来。
  6. 关闭套接字:clientSocket.close()当接收完服务器的响应后,关闭客户端的套接字以释放资源。

使用方法:

  1. 运行服务器(确保你的多线程服务器已经启动,并且有一个HTML文件可以被请求)。
  2. 在终端中运行客户端,指定服务器的主机名/IP地址、端口号以及文件路径。例如:python client.py localhost 6789 HelloWorld.html这里 localhost 是服务器主机,6789 是端口号,HelloWorld.html 是请求的文件名。
  3. 客户端将连接服务器,发送GET请求,并显示服务器的响应。如果文件存在,服务器会返回文件内容;如果文件不存在,服务器会返回404错误信息。

验证流程:

  1. 在服务器上放置 HelloWorld.html 文件,并启动服务器。
  2. 通过客户端请求该文件:python client.py localhost 6789 HelloWorld.html客户端应显示HTML文件的内容。
  3. 请求不存在的文件:python client.py localhost 6789 nonexistentfile.html客户端应收到“404 Not Found”的响应。

本文转载自: https://blog.csdn.net/FHKHH/article/details/142340239
版权归原作者 FHKHH 所有, 如有侵权,请联系我们删除。

“计算机网络第二章:作业 1: Web 服务器”的评论:

还没有评论