Python 实现 Web 静态服务器(HTTP 协议)
目录
- 一、在本地启动 HTTP 服务器
- 1. Windows 下安装 node.js
- 1)下载安装包
- 2)配置环境变量
- 3)安装镜像
- 4)node.js 的常用命令
- 2. 安装 http-server 服务
- 3. 使用 http-server 开启服务
- 1)使用 http-server
- 2)详解 http-server [path] [options]
- 二、Web 静态服务器
- 1. 显示固定的页面
- 1)Python 代码
- 2)结果展示
- 2. 显示请求的页面
- 1)Python 代码
- 2)结果展示
- 3. 多进程显示页面
- 4. 多线程显示页面
- 5. 非阻塞模式显示页面
- 6. 利用 epoll 显示页面(Linux 下运行)
- 7. 利用 gevent 显示页面
有关 HTTP 的基础知识:【应用层 IV(万维网WWW)【★★】】
一、在本地启动 HTTP 服务器
1. Windows 下安装 node.js
1)下载安装包
node.js 下载网址:【node.js 中文网】
-
进入官网,根据需求下载安装包,注意:
-
.msi 是 Windows 系统下的一种安装包文件格式,这种文件类型包含了某个软件或程序的所有安装信息和必要文件,用户只需按照提示进行安装,即可成功将软件或程序部署到 Windows 系统中。
-
.zip 是程序的压缩包,不需要进行安装,解压即可。
-
- 根据下图安装软件,可以自行修改安装路径。
-
安装好后,Win + X 点击 “系统” → “高级系统设置” → “环境变量(N)…” 并双击 “系统变量(S)” 下的 PATH 可以看到新增了一项
D:\nodejs\
(设置的安装路径)。 -
Win + R 输入 cmd 进入命令提示符,输入
node -v
和npm -v
,如果输出版本号,则说明 node.js 安装成功。
2)配置环境变量
-
在安装路径
D:\nodejs\
下新建两个文件夹 “node_global” 和 “node_cache” ,并复制它们的路径。 -
以管理员身份运行 cmd ,输入以下两条指令:
npm config set prefix "D:\nodejs\node_global"
npm config set cache "D:\nodejs\node_cache"
- 可以通过以下两条指令查看配置的路径:
npm config get prefix
npm config get cache
-
Win + X 点击 “系统” → “高级系统设置” → “环境变量(N)…”
-
双击 “用户变量(U)” 下的 PATH ,将
C:\Users\[用户名]\AppData\Roaming\npm
修改成D:\nodejs\node_global
; -
双击 “系统变量(S)” 下的 PATH ,新建
D:\nodejs\node_global\node_modules
和D:\nodejs\node_cache
,最后一路点击确定即可。
-
注:此时 node_global 文件夹下并无 node_modules 文件夹,没有关系,先进行设置即可。
- 配置完成后,全局安装一个 express 模块进行测试是否配置成功:以管理员身份运行 cmd ,输入指令
npm install express -g
,其中 -g 代表全局安装,此时 node_global 文件夹下才会自动创建一个名为 node_modules 的文件夹。
3)安装镜像
-
以管理员身份运行 cmd ,输入指令
npm config set registry https://registry.npmmirror.com
安装淘宝镜像。 -
输入指令
npm config get registry
查看是否安装成功。
【可选】如果想要使用 cnpm 命令行工具代替默认的 npm ,则进行以下操作:
-
以管理员身份运行 cmd ,输入指令
npm install -g cnpm --registry=https://registry.npmmirror.com
。 -
输入指令
cnpm -v
查看是否安装成功。
4)node.js 的常用命令
- 检查版本
# 检查 Node.js 版本
node -v# 检查 npm 版本
npm -v
- 初始化项目
# 初始化一个新的 Node.js 项目
npm init
- 安装和卸载包
# 安装特定版本的 Node.js 包
npm install <package-name>@<version># 全局安装 Node.js 包
npm install -g <package-name># 卸载 Node.js 包
npm uninstall <package-name>
- 查看已安装的包
# 查看全局安装的 Node.js 包
npm list -g --depth 0# 查看已安装的本地包
npm ls
- 更新包
# 更新所有全局安装的 Node.js 包
npm update -g# 更新特定 Node.js 包
npm update <package-name>
- 运行代码和启动应用程序
# 运行 Node.js 文件
node <filename.js># 使用 nodemon 启动应用程序(自动重启)
nodemon <filename.js># 在浏览器中打开应用程序
npm start# 指定环境变量启动应用程序
NODE_ENV=production node <filename.js>
参考文章:
【node.js安装及环境配置超详细教程【Windows系统安装包方式】】
【2024最新版Node.js下载安装及环境配置教程【保姆级】】
【Node.js安装及环境配置超详细教程【Windows系统】】
2. 安装 http-server 服务
http-server 是一个简单且快速的零配置命令行静态文件服务器,主要用于本地快速启动一个静态文件服务,它通常用于开发和测试环境。
-
以管理员身份运行 cmd ,输入指令
npm install http-server -g
进行全局安装 http-server 服务。 -
输入命令
http-server -v
可查看该服务是否安装成功。
3. 使用 http-server 开启服务
1)使用 http-server
-
Win + R 输入 cmd 进入命令提示符,通过
cd
操作将磁盘路径改至需要开启服务的路径下。 -
输入命令
http-server -p 7890
指定端口开启服务器。
默认的访问地址是:http://127.0.0.1:8080
-
启动成功可以通过 http://127.0.0.1:7890 进行访问。
-
按下 Ctrl + C 终止服务。
2)详解 http-server [path] [options]
以下是 http-server 常用的一些命令和参数:
参数 | 说明 | 示例 |
---|---|---|
[path] | 指定服务器根目录,默认当前目录 | http-server ./public |
-p or --port <port> | 指定监听端口,默认 8080 | http-server -p 3000 |
-a or --address <address> | 绑定的地址,默认 0.0.0.0(所有地址) | http-server -a 127.0.0.1 |
-c or --cache <time> | 缓存时间(秒) | http-server -c 3600 |
-c-1 | -1 表示禁用缓存 | http-server -c-1 |
-d or --directory | 启用目录列表显示 | http-server -d |
-i or --index <file> | 指定默认首页文件 | http-server -i index.html |
-h or --help | 显示帮助信息 | http-server -h |
-o or --open | 启动时自动在浏览器打开 | http-server -o |
-g or --gzip | 启用 gzip 压缩 | http-server -g |
-e or --ext <extension> | 设置默认扩展名,默认 html | http-server -e htm |
-s or --silent | 静默模式,不输出任何日志信息 | http-server -s |
-r or --robots <file> | 指定 robots.txt 文件位置 | http-server -r ./robots.txt |
--cors | 启用 CORS 跨域 | http-server --cors |
-S or --ssl | 启用 HTTPS | http-server --ssl --cert ./cert.pem --key ./key.pem |
-C or --cert <file> | SSL 证书文件路径 | http-server -S -C ./cert.pem -K ./key.pem |
-K or --key <file> | SSL 私钥文件路径 | http-server -S -C ./cert.pem -K ./key.pem |
-U or --utf8 | 对 URL 使用 UTF-8 编码 | http-server -U |
-P or --proxy <url> | 代理未找到的请求到指定 URL | http-server -P http://example.com |
参考文章:【http-server使用,启动本地服务器 & 使用serve包本地启动】
二、Web 静态服务器
1. 显示固定的页面
1)Python 代码
# 服务器端
import socketdef service_client(client_socket):# 1. 接收浏览器发送过来的请求 ,即 http 请求recv_data = client_socket.recv(4096).decode("utf-8")request_header_lines = recv_data.splitlines()for line in request_header_lines:print(line)# 2. 返回 http 格式的数据,给浏览器response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源response_headers += "\r\n" # 用一个空的行与 body 进行隔开response_body = "hello world"# 将 response header 和 response body 发送给浏览器response = response_headers + response_bodyclient_socket.send(response.encode("utf-8"))client_socket.close()def main():# 1. 创建 TCP 套接字server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用# 设置服务器端 4 次挥手之后资源能够立即释放,这样就保证下次运行程序时 可以立即绑定 8888 端口server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号,127.0.0.1 是本机对自己的网络地址,即本地回环地址ip = "127.0.0.1"port = 8888server_socket.bind((ip, port))print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))# 3. 变为监听套接字server_socket.listen(128)while True:# 4. 等待新客户端的链接client_socket, client_addr = server_socket.accept()# print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')# 5. 为这个客户端服务service_client(client_socket)if __name__ == "__main__":main()
实现步骤:
- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:8888” 并回车。
2)结果展示
右击浏览器页面,选择 “检查” → “网络” ,输入 Ctrl + R 刷新纪录,再单击想要查看的名称就可以查看详细的标头信息。
2. 显示请求的页面
1)Python 代码
import socket
import re
import time# 服务器端
def service_client(new_socket, time_start):# 1. 接收浏览器发送过来的请求 ,即 http 请求request = new_socket.recv(4096).decode("utf-8")# 得到头部的每一行request_lines = request.splitlines()if request_lines:# 使用正则表达式获取请求的 urlret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])# 例如:'GET /how-to-learn-qt.html HTTP/1.1'if ret:file_name = ret.group(1)# 例如:/how-to-learn-qt.htmlif file_name == "/":file_name = "/index.html"print(">" * 30, file_name) # 输出请求的 urlfor request_line in request_lines:print(request_line) # 输出请求# 2. 返回 http 格式的数据,给浏览器try:f = open("./../html" + file_name, "rb") # 打开请求的资源# 找不到资源就返回 404 Not Foundexcept FileNotFoundError:response = "HTTP/1.1 404 NOT FOUND\r\n" # headerresponse += "\r\n"response += "------File Not Found-----" # bodynew_socket.send(response.encode("utf-8"))else:response = "HTTP/1.1 200 OK\r\n" # headerresponse += "\r\n"html_content = f.read() # bodyf.close()new_socket.settimeout(5)try:# 2.1 将 response header 发送给浏览器new_socket.send(response.encode("utf-8"))# 2.2 将 response body 发送给浏览器new_socket.send(html_content)# 发送超时except socket.timeout:response = "HTTP/1.1 504 Gateway Timeout\r\n" # headerresponse += "\r\n"response += "------Send Timeout-----" # bodynew_socket.send(response.encode("utf-8"))# 关闭套接字new_socket.close()time_end = time.time()print(f'The total time spent is {time_end - time_start} seconds.')print('-' * 50)def main():# 1. 创建 TCP 套接字tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号ip = "127.0.0.1"port = 7890tcp_server_socket.bind((ip, port))print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))# 3. 变为监听套接字tcp_server_socket.listen(128)while True:# 4. 等待新客户端的链接new_socket, client_addr = tcp_server_socket.accept()# print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')time_start = time.time()# 5. 为这个客户端服务service_client(new_socket, time_start)# 关闭监听套接字# tcp_server_socket.close()if __name__ == "__main__":main()
HTML 资源下载:【Python 实现 Web 静态服务器中需要使用到的 HTML 资源】
实现步骤:
- Win + R 输入 cmd 进入命令提示符,通过
cd
操作将磁盘路径改至需要开启服务的路径下,并输入命令http-server -p 7890
指定 7890 端口开启服务器。- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。
2)结果展示
3. 多进程显示页面
Python 代码:
import socket
import re
import multiprocessing
import time# 服务器端
def service_client(new_socket, time_start):# 1. 接收浏览器发送过来的请求 ,即 http 请求request = new_socket.recv(4096).decode("utf-8")# 得到头部的每一行request_lines = request.splitlines()if request_lines:# 使用正则表达式获取请求的 urlret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])# 例如:'GET /how-to-learn-qt.html HTTP/1.1'if ret:file_name = ret.group(1)# 例如:/how-to-learn-qt.htmlif file_name == "/":file_name = "/index.html"print(">" * 30, file_name) # 输出请求的 urlfor request_line in request_lines:print(request_line) # 输出请求# 2. 返回 http 格式的数据,给浏览器try:f = open("./../html" + file_name, "rb") # 打开请求的资源# 找不到资源就返回 404 Not Foundexcept FileNotFoundError:response = "HTTP/1.1 404 NOT FOUND\r\n" # headerresponse += "\r\n"response += "------File Not Found-----" # bodynew_socket.send(response.encode("utf-8"))else:response = "HTTP/1.1 200 OK\r\n" # headerresponse += "\r\n"html_content = f.read() # bodyf.close()new_socket.settimeout(5)try:# 2.1 将 response header 发送给浏览器new_socket.send(response.encode("utf-8"))# 2.2 将 response body 发送给浏览器new_socket.send(html_content)# 发送超时except socket.timeout:response = "HTTP/1.1 504 Gateway Timeout\r\n" # headerresponse += "\r\n"response += "------Send Timeout-----" # bodynew_socket.send(response.encode("utf-8"))# 关闭子进程new_socket.close()time_end = time.time()print(f'The total time spent is {time_end - time_start} seconds.')print('-' * 50)def main():# 1. 创建 TCP 套接字tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号ip = "127.0.0.1"port = 7890tcp_server_socket.bind((ip, port))print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))# 3. 变为监听套接字tcp_server_socket.listen(128)while True:# 4. 等待新客户端的链接new_socket, client_addr = tcp_server_socket.accept()# print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')time_start = time.time()# 5. 为这个客户端服务p = multiprocessing.Process(target=service_client, args=(new_socket, time_start))p.start()# 关闭父进程new_socket.close()# 关闭监听套接字# tcp_server_socket.close()if __name__ == "__main__":main()
实现步骤:
- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。
4. 多线程显示页面
Python 代码:
import socket
import re
import threading
import time# 服务器端
def service_client(new_socket, time_start):# 1. 接收浏览器发送过来的请求 ,即 http 请求request = new_socket.recv(4096).decode("utf-8")# 得到头部的每一行request_lines = request.splitlines()if request_lines:# 使用正则表达式获取请求的 urlret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])# 例如:'GET /how-to-learn-qt.html HTTP/1.1'if ret:file_name = ret.group(1)# 例如:/how-to-learn-qt.htmlif file_name == "/":file_name = "/index.html"print(">" * 30, file_name) # 输出请求的 urlfor request_line in request_lines:print(request_line) # 输出请求# 2. 返回 http 格式的数据,给浏览器try:f = open("./../html" + file_name, "rb") # 打开请求的资源# 找不到资源就返回 404 Not Foundexcept FileNotFoundError:response = "HTTP/1.1 404 NOT FOUND\r\n" # headerresponse += "\r\n"response += "------File Not Found-----" # bodynew_socket.send(response.encode("utf-8"))else:response = "HTTP/1.1 200 OK\r\n" # headerresponse += "\r\n"html_content = f.read() # bodyf.close()new_socket.settimeout(5)try:# 2.1 将 response header 发送给浏览器new_socket.send(response.encode("utf-8"))# 2.2 将 response body 发送给浏览器new_socket.send(html_content)# 发送超时except socket.timeout:response = "HTTP/1.1 504 Gateway Timeout\r\n" # headerresponse += "\r\n"response += "------Send Timeout-----" # bodynew_socket.send(response.encode("utf-8"))# 关闭子线程new_socket.close()time_end = time.time()print(f'The total time spent is {time_end - time_start} seconds.')print('-' * 50)def main():# 1. 创建 TCP 套接字tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号tcp_server_socket.bind(("127.0.0.1", 7890))# 3. 变为监听套接字tcp_server_socket.listen(128)while True:# 4. 等待新客户端的链接new_socket, client_addr = tcp_server_socket.accept()# print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')time_start = time.time()# 5. 为这个客户端服务p = threading.Thread(target=service_client, args=(new_socket, time_start))p.start()# 多线程时,new_socket 传递给子线程以后,主线程不能关闭# new_socket.close()# 关闭监听套接字# tcp_server_socket.close()if __name__ == "__main__":main()
实现步骤:
- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。
5. 非阻塞模式显示页面
Python 代码:
import time
import socket
import sys
import re# 定义一个 WSGI 服务器的类
class WSGIServer(object):def __init__(self, ip, port, documents_root):# 1. 创建套接字self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定本地信息self.server_socket.bind((ip, port))# 3. 变为监听套接字self.server_socket.listen(128)# 4. 设置非阻塞self.server_socket.setblocking(False)# 客户列表self.client_socket_list = list()# 静态资源的路径self.documents_root = documents_rootdef run_forever(self): # 运行服务器while True:# 等待对方链接try:new_socket, new_addr = self.server_socket.accept()# 未有浏览器链接except BlockingIOError:passelse:time_start = time.time()# 设置非阻塞new_socket.setblocking(False)# 添加客户至客户列表self.client_socket_list.append([new_socket, new_addr, time_start])print(f'New client ({new_addr[0]} : {new_addr[1]}) link success')# 遍历列表中的连接,如果有浏览器发过来数据,那么就处理for client_socket in self.client_socket_list:try:request = client_socket[0].recv(4096).decode('utf-8')except BlockingIOError:passelse:if request: # 有数据就处理数据print(f'Client [{client_socket[1][1]}] is working')self.deal_with_request(request, client_socket[0])else: # 客户与浏览器断开client_socket[0].close()time_end = time.time()print(f'Client [{client_socket[1][1]}] finish request')print(f'The total time spent is {time_end - client_socket[2]} seconds.')self.client_socket_list.remove(client_socket)def deal_with_request(self, request, client_socket): # 处理数据request_lines = request.splitlines()# 例如:'GET /how-to-learn-qt.html HTTP/1.1'ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])if ret:# 例如:/how-to-learn-qt.htmlfile_name = ret.group(2)if file_name == "/":file_name = "/index.html"print(">" * 30, file_name)for i, line in enumerate(request_lines):print(i, line)# 读取文件数据try:f = open(self.documents_root + file_name, "rb")except PermissionError:response_body = "file not found,Please enter the correct URL"response_header = "HTTP/1.1 404 not found\r\n"response_header += "Content-Type: text/html; charset=utf-8\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将 header 返回给浏览器client_socket.send(response_header.encode('utf-8'))# 将 body 返回给浏览器client_socket.send(response_body.encode("utf-8"))else:content = f.read()f.close()response_body = contentresponse_header = "HTTP/1.1 200 OK\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将 header 和 body 返回给浏览器client_socket.send(response_header.encode('utf-8') + response_body)# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./../html"def main(): # 控制 web 服务器整体if len(sys.argv) == 3:ip = sys.argv[1]port = sys.argv[2]if port.isdigit():port = int(port)print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))http_server = WSGIServer(ip, port, DOCUMENTS_ROOT)http_server.run_forever()else:print('The port number was entered incorrectly')else:print("Run the command : python3 4.non-blocking.py 127.0.0.1 7890")if __name__ == "__main__":main()
实现步骤:
- 图形化界面或命令行传参 “127.0.0.1 7890”
- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。
“BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作” 的解决方法见:【Python BlockingIOError 阻塞错误】
6. 利用 epoll 显示页面(Linux 下运行)
Python 代码:
# !/usr/bin/python
# -*- coding:utf-8 -*-import socket
import select
import reBASE_PATH = './../html'# 服务器端
def service_client(new_socket, request):request_lines = request.splitlines()if not request_lines:returnret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])if ret:file_name = ret.group(1)if file_name == "/":file_name = "/index.html"print("*" * 50, file_name)print(request)# 2. 返回 http 格式的数据,给浏览器try:f = open(BASE_PATH + file_name, "rb")except FileNotFoundError:response = "HTTP/1.1 404 NOT FOUND\r\n"response += "\r\n"response += "------file not found-----"new_socket.send(response.encode("utf-8"))else:html_content = f.read()f.close()response_body = html_contentresponse_header = "HTTP/1.1 200 OK\r\n"response_header += "Content-Length:%d\r\n" % len(response_body)response_header += "\r\n"response = response_header.encode("utf-8") + response_bodynew_socket.send(response)def main():# 1. 创建套接字tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号ip = "192.168.200.128"port = 7890tcp_server_socket.bind((ip, port))print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))# 3. 变为监听套接字tcp_server_socket.listen(128)# 将套接字变为非堵塞tcp_server_socket.setblocking(False)# 4. 创建一个epoll对象epl = select.epoll()# 将监听套接字对应的 fd 注册到 epoll 中# 注册并监控 tcp_server_socket, select.EPOLLIN 为可读事件epl.register(tcp_server_socket.fileno(), select.EPOLLIN)fd_event_dict = dict()while True:# 默认会堵塞,直到 os 监测到数据到来 通过事件通知方式告诉这个程序,此时才会解堵塞# 轮询注册的事件fd_event_list = epl.poll()# [(fd, event), (套接字对应的文件描述符, 这个文件描述符到底是什么事件)]for fd, event in fd_event_list:# 等待新客户端的链接if fd == tcp_server_socket.fileno():new_socket, client_addr = tcp_server_socket.accept()# 注册并监控 new_socketepl.register(new_socket.fileno(), select.EPOLLIN)fd_event_dict[new_socket.fileno()] = new_socket# 判断已经链接的客户端是否有数据发送过来elif event == select.EPOLLIN:recv_data = fd_event_dict[fd].recv(4096).decode("utf-8")# 有数据则处理数据,通过不遍历来定位 socketif recv_data:service_client(fd_event_dict[fd], recv_data)# 客户与浏览器断开else:fd_event_dict[fd].close()# 取消注册epl.unregister(fd)# 从字典中移除del fd_event_dict[fd]# 关闭监听套接字# tcp_server_socket.close()if __name__ == "__main__":main()
实现步骤:
- 开启虚拟机,然后配置 PyCharm 连接远程服务器,并切换 Python 解释器。
- 将需要运行的 .py 代码和文件部署上传至 Linux 的文件夹下(右键文件 → “部署” → “上传到…”)。
- 运行上述服务器端代码(注意这时的 IP 地址并非本地回环地址,而是 Liunx 的 IPv4 地址)。
- 在浏览器的地址栏里输入 “http://192.168.200.128:7890” 并回车。
配置 PyCharm 连接远程服务器的步骤见:【Python-简单网络编程 I】
7. 利用 gevent 显示页面
Python 代码:
import gevent
from gevent import monkeymonkey.patch_all()import socket
import reBASE_PATH = './../html'def service_client(new_socket):# 接收 http 请求request = new_socket.recv(4096).decode('utf-8')if request:request_lines = request.splitlines()ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])if ret:file_name = ret.group(1)if file_name == "/":file_name = "/index.html"print(">" * 30, file_name)print(request) # 输出请求try:f = open(BASE_PATH + file_name, "rb")except FileNotFoundError:response = "HTTP/1.1 404 NOT FOUND\r\n"response += "\r\n"response += "-------file not found-------"new_socket.send(response)else:html_content = f.read()f.close()response = "HTTP/1.1 200 OK\r\n"response += "\r\n"new_socket.send(response.encode('utf-8'))new_socket.send(html_content)new_socket.close()def main():# 1. 初始化tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 确保端口复用tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定 IP 和端口号ip = "127.0.0.1"port = 7890tcp_server_socket.bind((ip, port))print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))# 3. 变为监听套接字tcp_server_socket.listen(128)while True:# 4. 等待新客户端的链接new_socket, socket_addr = tcp_server_socket.accept()# print(f'New client ({socket_addr[0]} : {socket_addr[1]}) link success')# 创建一个普通的 greenlet 对象并切换gevent.spawn(service_client, new_socket)# tcp_server_socket.close()if __name__ == '__main__':main()
实现步骤:
- 运行上述服务器端代码。
- 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。
相关文章:

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...