如果我在端口 5000 上以开发模式运行一个简单的 flask 应用程序(因此 Web 服务器和 Web 应用程序是 flask 库)。有没有办法查看客户端和服务器之间交换的 SYN、SYN-ack、ack 请求?例如,打印它们?我不想使用 Wireshark 之类的工具来查看流量,我想知道谁在处理该过程。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello'
当我执行curl localhost:5000/
curl 时,它首先发送 SYN?谁会应答它?Flask?
我该怎么做才能在自己的应用程序上看到所有这些过程?另外,我想看看 HTTP/1.1 的保持活动机制是如何工作的?
答案1
TCP 握手是否由 Web 应用程序 Flask 或 nginx 等 Web 服务器处理?
都不是。对于 TCP,握手完全由操作系统处理。
cURL 使用操作系统提供的“套接字 API”(通常是 socket() 和 connect() 函数)发起连接,而 Flask 的 devel-server 使用 listen() 和 accept() 接收连接。当 accept() 函数返回时,整个 TCP 握手已经完成,Flask 可以使用套接字来传输数据。
这意味着您尝试查看的进程实际上根本不是您应用程序的一部分。 Flask 仅使用 Python 的socket.socket()
对象,然后直接调用相应的 OS 函数 - 您需要使用 OS 跟踪工具(例如 dtrace/bpftrace/systemtap)来查看发生的内部内核调用。
但请注意,QUIC(如 HTTP/3)的工作方式略有不同——操作系统中没有直接支持 QUIC,因此它是cURL 和 Nginx 执行 QUIC 握手,尽管它们仍使用 ngtcp2 或 MsQuic 等库来完成所有工作。(端点仍使用“套接字 API”与操作系统通信,但对于 QUIC,它们会创建不实现自身握手的 UDP 套接字。)
我如何手动向本地网络中的计算机发送 SYN 数据包?
与数据包捕获工具类似,还有数据包生成工具可让您制作自定义数据包(甚至可能制作自己的 TCP 堆栈)。其中之一是scapy
,它是用 Python 编写的,可让您使用 Python REPL 手动构建数据包。另一个类似的选项是模块pypacker
,同样用 Python 编写。
此类工具通常使用“原始套接字”,它与 UDP 套接字非常相似,但应用程序可以包含自己的 IP 标头。您通常需要 root 权限才能使用此功能。
另外,我想看看 HTTP/1.1 的保持活动机制是如何工作的? [...] Flask 如何告诉操作系统保持 TCP 连接以便将其用于其他请求?
一般来说,一旦 TCP 连接打开,它就会仍然开放除非进程故意关闭它(或除非进程退出,在这种情况下操作系统会关闭连接)。操作系统不会跟踪“请求”——这是应用程序的问题;TCP 可以随时向任何方向传输数据。
因此,整个 HTTP/1.1“保持活动”机制是服务器在发送响应后实际上不会关闭 TCP 连接,即它不会调用shutdown()或close()。这允许客户端通过该连接发送更多HTTP请求。仅此而已——客户端和服务器都不会做出任何努力保持连接仍然有效,他们只是避免首先终止它。
(有一个可选的 HTTP标题它允许 HTTP/1.0 客户端请求保持活动,或者允许 HTTP/1.1 客户端阻止它。)
当然,该机制仅适用于在请求之间保持运行(并保持 OS 套接字打开)的客户端。例如,可以为curl
或wget
CLI 工具提供多个要下载的 URL,在为第一个 URL 建立连接后,它们将保持打开状态以进行下一个 URL(假设它来自同一服务器)。但是,一旦工具退出(curl
第二次运行),连接就会立即关闭有建立全新的联系。
类似地,Python将使用“keep-alive”自动在多个或调用requests.Session()
之间重复使用连接。但是,如果脚本退出,连接也会随之关闭。.get()
.post()
但请注意,有些其他协议还具有“保持活动”功能,其工作方式不同,通过发送明确的“我仍在运行”数据包(例如,可能每分钟发送一次保持活动数据包)。这可以通过两种方式之一实现 - 要么数据包携带应用程序生成的实际数据,或者它们是操作系统在应用程序启用此功能后发送的 0 长度 TCP 数据包。(后者称为“TCP keepalive”;它们不需要保持两台主机之间的正常连接打开,但有时用于防止中间的系统(例如家庭 NAT 路由器)强行关闭它。)