我的包含
msg = client_socket.recv(1024).decode()
的内部 while 循环无法在循环内没有 client_socket, addr = server_socket.accept()
的情况下处理多个客户端请求。我不知道如何才能继续为所有请求重复使用相同的连接。
from socket import *
import sys
def run_server(port):
# Create server socket
server_socket = socket(AF_INET, SOCK_STREAM)
# Bind the socket to a specific address
server_socket.bind(('localhost', port))
# Argument 1 represents the maximum number of queued connections
server_socket.listen(1)
print(f"Serving on port {port}")
while 1:
# Accept incoming connection from client
client_socket, addr = server_socket.accept()
print(f"Connection established from address {addr}")
while 1:
# Receive data from client
msg = client_socket.recv(1024).decode()
# Extract file name
file_name = msg.split('\n')[0].split()[1].replace('/', '')
# Process client requests
try:
with open(file_name, 'rb') as file:
status_line = "HTTP/1.1 200 OK\r\n"
file_extension = file_name.split('.')[-1].lower()
if file_extension == 'html':
content_type_header = "Content-Type: text/html\r\n\r\n"
elif file_extension == 'png':
content_type_header = "Content-Type: image/png\r\n\r\n"
content = file.read()
# Combine status line, headers and content
http_response = (status_line + "Connection: keep-alive\r\n" + content_type_header).encode() + content
except OSError as _:
# File not found, generate a 404 response
status_line = "HTTP/1.1 404 Not Found\r\n"
content_type_header = "Content-Type: text/html\r\n\r\n"
content = "<h1>404 Not Found</h1><p>The requested file was not found on this server.</p>"
http_response = (status_line + content_type_header + content).encode()
# Send HTTP response back to client
client_socket.sendall(http_response)
# Close the connection after handling multiple requests
client_socket.close()
print("Connection closed")
server_socket.close()
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python WebServer.py <port>")
sys.exit(1)
# Get port number from command line parameter
port = int(sys.argv[1])
run_server(port)
我假设您这样做是为了学习,因为实现 HTTP 协议很复杂。
TCP是字节流协议,需要遵循HTTP协议来决定如何读取流以及读取多少。由于 HTTP 请求包含以
\r\n
结尾的请求和标头行以及指示标头结束的空行,因此通过 socket.makefile
将 TCP 套接字包装在类似文件的对象中可以更轻松地处理请求。
要实现保持活动状态,标头需要一个
Content-Length
标头。
下面我做了一些最小的更改来支持您的实施。它绝不是完整的。在与下面的脚本相同的目录中创建一个
root
目录,并在其中填充一些 HTML 和 PNG 文件,例如 index.html
或 icon.png
。将浏览器指向 http://localhost:8080/index.html
或 http://localhost:8080/icon.png
,文件应显示在浏览器中。至少他们在 Chrome 上为我做了 😊.
import socket
import sys
import os
from pprint import pprint
def run_server(port):
with socket.socket() as server_socket:
server_socket.bind(('localhost', port))
server_socket.listen()
print(f'Serving on port {port}')
while True:
client_socket, addr = server_socket.accept()
# wrap client socket in a file-like object. Gives access to .readline().
with client_socket, client_socket.makefile('rb') as infile:
print(f'{addr}: connected')
while True:
# Receive HTTP request
line = infile.readline()
if not line: break
request = line.decode('ascii')
print(request)
# Read headers line-by-line and add to dictionary
headers = {}
while line := infile.readline():
if line == b'\r\n': break
key, _, value = line.decode('ascii').partition(':')
headers[key.strip()] = value.strip()
pprint(headers)
op, path, protocol = request.split()
print(op, path, protocol)
# hacky way to look under a directory called root below the webserver script.
file_name = os.path.abspath(os.path.join('root/.' + path))
print(file_name)
# minimally handles a GET for html and png files.
try:
with open(file_name, 'rb') as file:
http_response = b'HTTP/1.1 200 OK\r\n'
file_extension = file_name.split('.')[-1].lower()
if file_extension == 'html':
http_response += b'Content-Type: text/html\r\n'
elif file_extension == 'png':
http_response += b'Content-Type: image/png\r\n'
content = file.read()
except FileNotFoundError as e:
# File not found, generate a 404 response
http_response = b'HTTP/1.1 404 Not Found\r\n'
http_response += b'Content-Type: text/html\r\n'
content = b'<h1>404 Not Found</h1><p>The requested file was not found on this server.</p>'
# Send keepalive and the length of the content.
http_response += b'Connection: keep-alive\r\n'
http_response += f'Content-Length: {len(content)}\r\n'.encode('ascii')
# Send blank line (end of headers) and content.
http_response += b'\r\n' + content
client_socket.sendall(http_response)
print(f'{addr}: disconnected')
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python WebServer.py <port>')
sys.exit(1)
# Get port number from command line parameter
port = int(sys.argv[1])
run_server(port)