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()