计算机网络连接是指连接两台或多台计算设备(如台式计算机、移动设备、路由器或应用程序)以实现信息与资源的传输和交换的流程。
现在最常用的 以太网 是物理上使用星形拓扑(有一个路由器或集线器作为中心), 逻辑上使用总线拓扑(使用CSMA/CD的总线技术)
time ## 其它 ### 带宽 和 宽带 和 位宽 的联系与区别
带宽(Bandwidth)
bit per second
位 每 秒
每秒能传输的位,或者叫做 比特每秒 每秒能传输的比特
代理 proxy
「代为处理」
代理的类型和作用域
在各种系统上的代理设置
windows
linux
安卓
正向代理/转发代理 forward proxy
反向代理 reverse proxy
网关 gateway
「网络关口」「网络海关」
隧道 tunnel
端口转发 port forwarding
虚拟专用网 (Virtual Private Network , VPN)
虚拟私有云 (Virtual Private Cloud , VPC)
透明 transparent
<!--
一般情况下如何配置ip
ip地址 子网掩码 网关 dns
两台电脑通过网线直连的情况下 ip 要怎么配置?
三台电脑只有集线器的情况下 ip 要怎么配置?
如何实现一个web服务器
拓扑结构
星型
环型
总线
以太网
...
模型
七层模型 (由 OSI 提出的)
应用层 Application
表示层 Presentation
负责转化数据格式,并处理数据加密和数据压缩。
其实 tls 这这种协议可以归类到这一层里
会话层 Session
主要是用来管理网络设备的会话连接,建立会话,保持会话,断开会话
传输层 Transport
网际层 Internet/Network
数据链路层 Link/Data link/Network interface
物理层 Hardware/Physical
四层模型 ip/tcp
应用层
传输层
网际层
网络接口层
五层模型
应用层 -> data (数据)
传输层 ->
TCP -> segment (段)
UDP -> datagram (数据报文)
网际层 -> packet (包)
数据链路层 -> frame (帧)
物理层 -> bit (比特)
各层常用的协议
应用层
http
0.9 1.0 1.1 2 3
mqtt
telnet
rfc 97 137 153 318
854 855 856 857 859 860 861
2217
ftp
dns
nntp
网络新闻传输协议(Network News Transfer Protocol)
USENET 和 NNTP 的关系,大概就是 网站 和 http 的关系差不多
irc
xmpp
ntp (Network Time Protocol) 网络时间协议
PTP (Precision Time Protocol) 精确时间协议
dhcp
DHCP(Dynamic Host Configuration Protocol)动态主机配置协议
DHCP 是基于客户端-服务器模式的标准协议,由RFC 2131定义
它允许服务器集中管理IP地址池,自动为网络中的主机(如计算机、手机等)分配临时或永久的IP地址及相关配置
服务器控制一段IP地址范围(地址池),客户端接入网络时自动获取IP地址、子网掩码、网关和DNS服务器等信息
DHCP采用四步交互流程(DORA模型)完成地址分配:
Discover :客户端广播请求,寻找可用的DHCP服务器。
客户端在未获得 IP 地址时,会通过 UDP 广播 (目标地址 255.255.255.255 或子网广播地址)发送 DHCP Discover 消息,寻找可用的 DHCP 服务器。
此时客户端完全不知道服务器的存在,因此使用广播确保消息能被同一链路中的所有设备(包括潜在的 DHCP 服务器)接收。
Offer :服务器响应并提供IP地址及相关配置。
Request :客户端正式请求使用提供的地址。
Acknowledge :服务器确认分配,完成绑定
DHCP客户端和服务器通过UDP端口68(客户端)和67(服务器)通信
WHOIS
RDAP (Registration Data Access Protocol, 注册数据访问协议)
smtp
pop3
imap
echo
discard
daytime
chargen
time
传输层
tcp
建立连接 三次握手
客户端 -> 服务端 syn
服务端 -> 客户端 syn+ack
客户端 -> 服务端 ack
为什么是三次握手?
如果是两次握手会有什么问题?
断开连接 四次挥手
主动关闭 -> 被动关闭 fin
被动关闭 -> 主动关闭 ack
被动关闭 -> 主动关闭 fin
主动关闭 -> 被动关闭 ack
主动关闭的那一边,在发送 ack 后还会等待 2msl 才会进入 closed 状态
被动关闭的那一边,在收到 ack 后就会立即进入 closed 状态
多数情况下都是客户端主动关闭的
为什么是四次握手?
如果少于四次握手会有什么问题?
为什么发送 ack 后还要等待 2msl 才会进入 closed 状态?
状态
LISTEN SYN-SENT SYN-RECEIVED
ESTABLISHED
FIN-WAIT-1 FIN-WAIT-2 CLOSE-WAIT LAST-ACK TIME-WAIT CLOSED
标志符
syn ack fin rst
发送 rest ,会立即释放连接,会丢弃发送缓冲区和接收缓冲区的数据,不需要等待对方ack确认
流量控制
滑动窗口
拥堵控制
慢启动 与 拥塞避免
TCP 慢启动 / 14KB 规则 ?
快重传 与 快恢复
三个半事件
连接建立
连接断开
消息到达
消息发送完毕(这是半个事件)
沾包
根本原因
什么情况下会发生
解决方式
为什么udp没有沾包
tcp的异常中断
程序崩溃
操作系统会代为发送四次挥手
主机宕机
没有重启
发送端在发送数据等待ack超时后,会强制关闭连接
有重启
接收端回复 rest 报文,然后重新建立连接
还要考虑 KeepAlive ?
可以参考主机宕机的情况
如果发送端一直没有发送数据,且没有keepalive,且应用层没有心跳,那么发送端的连接会一直保持,会浪费一点内存
其实只要重启的速度足够快,且 连接的socket 重新载入内存里,那么另一侧是不会感受到连接中断过的
如果客户端的ip有变化,例如 手机网络经过切换
那么,服务端会返回一个 rest ,然后重新建立连接
如果客户端访问服务端没有开放的端口
服务端可能会丢弃客户端的请求,然后客户端等待超时后自己关闭连接
服务端也可能直接返回 rest 包
这种请求也有可能被前面的防火墙挡下来了,根本到不了监听端口的服务端程序
udp
udp 协议是无连接的,不需要握手建立连接,数据发送后,服务器端不会返回确认信息
udp 的数据包发出去就不管的了,没有握手,挥手,ack
也可以在应用层里实现 tcp 的那套可靠传输机制
icmp
ping traceroute/tracert
网际层
ip v4 v6
ARP
ARP inARP GARP RARP
IPsec
数据链路层
以太网 (Ethernet)
以太网帧又分为 Ethernet II 和 IEEE 802.3
关注 Ethernet II 就可以了
帧间隙 来区分不同的 帧
各层对应的硬件
网关 (gateway)
路由器 (route)
交换器/交换机 (switch)
桥接器/网桥 (network bridge)
集线器 (ethernet hub 又或者 简称 hub)
中继器 (repeater)
调制解调器 (modem modulator-demodulator 调制器-解调器)
和安全相关的硬件
IPS (Intrusion Prevention System) 入侵检测(旁路部署)
IDS (Intrusion Detection System) 入侵防御(串行部署)
FW (FireWall) 防火墙
WAF (Web Application FireWall) 网页应用防火墙
UTM (Unified Threat Management) 统一威胁管理
按照规模划分的网络类型
pan
lan
vlan
wlan
can
man
wan
更多
ban
san
han
ran
Backbone
ipx
从两个计算机直连到广域网
载波侦听多路访问
Carrier Sense Multiple Access CSMA
载波侦听多路访问/碰撞检测(CSMA/CD)
CSMA with Collision Detection
载波侦听多路访问/碰撞避免(CSMA/CA)
CSMA with Collision Avoidance
路由表和路由算法
静态路由
动态路由
迪达拉算法
距离向量路由协议(Distance Vector Routing Protocol):
RIP(Routing Information Protocol)和IGRP(Interior Gateway Routing Protocol),它们使用跳数作为衡量指标。
链路状态路由协议(Link State Routing Protocol):
OSPF(Open Shortest Path First)和IS-IS(Intermediate System to Intermediate System),它们通过交换链路状态信息来构建网络拓扑图。
tcp 和 udp
什么是socket
socket 的中文翻译是套接字
network socket 网络套接字
五元组(five-element tuple): 源地址 源端口 目标地址 目标端口 传输层协议
一个五元组,优惠称为套接字对(socket pairs)
SOCK_DGRAM 数据报套接字 用在UDP
SOCK_STREAM 流套接字 用在TCP
SOCK_RAW 原始套接字 除了UDP和TCP之外的传输层协议,例如 icmp
从socket里读写内容
一些限制
输入输出只考虑 asni ,多字节编码不好处理,这里只关注网络连接
一次通讯数据包最大的长度为 255 ,因为分包操作也挺麻烦的
基本套路
建立连接
socket
setsockopt
服务端
bind
listen
accept
客户端
connect
发送和接收数据
read / write -> 通用的文件描述符操作函数
recv / send -> TCP
recvfrom / sendto -> UDP
关闭连接
shutdown -> 一般只用于socket
可以选择关闭读 关闭写 关闭读写,调用后 socket 就会关闭
close -> 通用的文件描述符操作函数
调用后无法读写,但 socket 未必会关闭,
调用后 socket 的引用计数会减1,只有当 socket 的引用计数为0时才会关闭连接
比较保险的方式是先调用 shutdown 再调用 close
io复用
select poll epoll
只输出一个 helloworld
能接收多次连接的 helloworld
能同时接收多个连接的 helloworld
实现最简单的五个协议
echo (RFC 862) 7 回显服务,把收到的数据发回客户端
discard (RFC 863) 9 丢弃所有收到的数据
chargen (RFC 864) 19 服务端 accept 连接之后,不停地发送测试数据
daytime (RFC 867) 13 务端 accept 连接之后,以字符串形式发送当前时间,然后主动断开连接
time (RFC 868) 87 服务端 accept 连接之后,以二进制形式发送当前时间的32位时间戳,然后主动断开连接
只考虑 tcp 的
用 netcat 作为客户端
然后是基于 telnet 的 echo
然后是基于 telnet 的聊天室
nc telnet http proxy
http 服务器
http 协议
0.9 1.0 1.1 2 3
0.9 没有 rfc ,协议十分简陋,基本可以忽略
1.0 定义了http协议的主要内容
1.1 多了 保持连接 Connection: keep-alive
2 将所有数据切分为二进制帧,一个连接可以发送多个请求
3 使用 udp 协议,强制使用 tls , http2是允许明文传输的,但http3是强制的
http3 就是 quic
quic 看起来是 udp ,其实是用户态的 tcp2.0+tls1.3+http2
Quick UDP Internet Connections
快速 UDP 网络 连接
请求码 和 响应码
mime
缓存
基本套路
建立连接
接收数据
解释请求
请求行
请求方法 GET POST PUT DELETE OPTIONS
路径
http版本号 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3
都只能是 ascii 编码
三个部分用空格隔开
和 请求头 用 /r/n 隔开
例子 GET /index.html HTTP/1.0
请求头
是一组键值对
键 和 值 用 一个分号和一个空格隔开 :
每个键值对用 /r/n 隔开
键 是不区分大小写的
键 和 值 都只能是 ascii 编码
和 请求体 用 /r/n/r/n 隔开
比较关键的几个键
host 指明请求的域名
content-length 请求体的长度,单位是字节
请求体
理论上什么类型的数据都可以
常见的类型
application/x-www-form-urlencoded
multipart/form-data
application/json
text/plain
application/xml
application/octet-stream
处理数据
构造响应
响应行
http版本号 HTTP/1.0
状态码 200
原因短语 OK
一般和状态码是固定搭配
都只能是 ascii 编码
三个部分用空格隔开
和 请求头 用 /r/n 隔开
例子 HTTP/1.0 200 OK
响应头
和请求头一样
响应体
理论上什么类型的数据都可以
输出响应
根据实际情况决定 关闭连接 或 保持连接
进程模型
单进程单线程
单进程多线程
多进程单线程
多进程多线程
io 多路复用
select
poll
epoll
水平触发 (level trigger, LT)
边缘触发 (edge trigger, ET)
具体类型
静态
动态
cgi
RFC 3875 https://datatracker.ietf.org/doc/html/rfc3875
https://www.w3.org/CGI/
https://www.w3.org/Daemon/User/CGI/Overview.html
https://www.zx.net.nz/mirror/hoohoo.ncsa.uiuc.edu/cgi/overview.html
fastcgi
https://fastcgi-archives.github.io/
代理
socket 双写
位置
反向代理
正向代理
类型
http代理
RFC 2616 7230 7231 7235
sockt代理
RFC 1928 SOCKS5
pac
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Proxy_servers_and_tunneling
websocket
WebSocket Secure (wss)
rfc 6455 定义了 WebSocket 协议的核心规范(握手、数据帧、掩码、控制帧等)
rfc 8441 讨论在 HTTP/2 上启动 WebSocket 的方法(以增强与 HTTP/2 的兼容)
相关的rfc
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Resources_and_specifications
认证 和 授权
流量控制
TLS
1.2
1.3
OpenNIC
OpenNIC(或称OpenNIC Project)是由用户所有和控制的[1][2]顶级域名的域名注册局,其提供类似ICANN的传统顶级域名的非国家性替代。
这里的域名往往会被用在 暗网 和 深网
tor (The Onion Router) 洋葱路由
i2p (Invisible Internet Project) 隐形的 互联网 项目
tor i2p 和 OpenNIC的域名,这三个通常会配合地使用
表网 -> 表层网络 Surface Web
深网 -> 深层网络 Deep web
暗网 -> 黑暗网络 Dark web
明网 -> Clearnet
虽然不是很严谨
能被搜索引擎收录的是 表网
不能被搜索引擎收录的是 深网
深网 包含 暗网
暗网是由深网的一小部分所构成的。
暗网一般需要特别的工具才能进入
和 暗网 相对的是明网
找到标准文档
看懂标准文档
按着文档写代码
测试写的程序是否符合文档的描述,比较简单的方式就是和已有的软件结合起来运行,例如,写了个http服务,就用浏览器访问一下,写了个fascgi,就用nginx连接一下
CGI 最早出现在NCSA HTTPd
NCSA HTTPd 是 1990 年代早期最流行的 Web 服务器软件之一,由美国国家超级计算应用中心(NCSA)开发,
是 Apache HTTP Server 的直接前身(Apache 最初是“a patchy server”,即对 NCSA HTTPd 的一系列补丁集合)
NCSA HTTPd
National Center for Supercomputing Applications HTTP Daemon
同一时期,除了服务器,还有浏览器 NCSA Mosaic
NCSA Mosaic 是第一个可以同时显示 图片 和 文字 的浏览器
WorldWideWeb 虽然也可以显示图片,但是是单独窗口中打开
Mosaic n. 马赛克
NCSA HTTPd 的最后一个正式版本是 1.5,发布于 1995 年(具体为 1995 年 6 月左右/中期 1995 年)
1.5.2a?
第一个版本的 apache 是基于 NCSA httpd 1.3 在1995 年 4 月发布,版本号是 0.6.2
95 7 0.7
95 8 0.8
95 12 1.0
98 06 06 1.3
99 年成立了 apache组织
02 04 06 2.0
05 12 01 2.2
12 02 21 2.4
Spyglass公司从NCSA获得技术和商标授权,用于开发自己的网页浏览器,但从未使用任何NCSA Mosaic源代码。
微软于1995年以200万美元获得Spyglass Mosaic的授权进行了修改,并重命名为Internet Explorer。
但后来因为专利方面的纠纷,微软向Spyglass赔偿了800万美元。
在1995年的用户指南The HTML Sourcebook: The Complete Guide to HTML,特别指出,
在一个名为Coming Attractions的章节中,Internet Explorer“将以Mosaic程序为基础”
虽然网景的创始人都是 NCSA Mosaic ,但网景浏览器并没有使用 NCSA Mosaic 的代码
Cookie Frames JavaScript SSL/TLS 都是最先出现在网景浏览器的
第一个浏览器 WorldWideWeb (后改名为Nexus,以避免与万维网混淆)
第一个http服务器 CERN httpd(也称为WorldWideWeb Daemon)
WorldWideWeb 从一开始就有图形界面
Line Mode Browser(也称为LMB、WWWLib或者www)是世界上第二个网页浏览器
Line Mode Browser 是第一个命令行浏览器,也是第一个可以移植的浏览器
WorldWideWeb 和 Line Mode Browser 的作者都是 蒂姆·伯纳斯-李
第一代 WorldWideWeb
第二代 NCSA Mosaic
第三代 网景 和 IE
NCSA Mosaic 时代就支持通过插件 调用外部程序 播放 音频 视频,但无法内联渲染
Netscape Navigator 2.0 引入 <embed> 标签,用于嵌入外部内容,例如 音频 视频 交互内容 (Java applets 和 Shockwave 动画 和 flash)
同一时期 IE 引入 <object> 标签,通用资源嵌入(图像、视频、插件、HTML等)
<object> 标签在 1999 被纳入标准
<embed> 标签 要等到 html5 才被纳入标准
java的第一个版本就有 applets 了
QuickTime RealPlayer Windows Media Player
浏览器原生支持 音视频播放都要等到html5时代
当进来一个请求时,web服务器把环境变量和这个页面请求通过一个socket比如FastCGI进程与web服务器(都位于本地)或者一个TCP 请求(FastCGI进程在远端的server farm)传递给FastCGI进程。[1]
服务传入请求时,网络服务器通过Unix域套接字、命名管道或TCP连接向FastCGI进程发送环境变量信息和页面请求。
响应通过相同的连接从进程返回到网络服务器,然后网络服务器将该响应传递给最终用户。
连接可能在响应结束时关闭,但是web服务器和FastCGI服务进程都将持续,不会被销毁。[2]
每个单独的FastCGI进程在其生命周期内可以处理许多请求,从而避免了每个请求进程创建和终止的开销。
并发处理多个请求可以通过几种方式来完成:通过内部多路复用使用一个连接(即一个连接上的多个请求);通过使用多个连接;或者通过这些方法的混合。
可以配置多个FastCGI服务器,提高稳定性和可扩展性。
当进来一个请求时,Web 服务器把环境变量和这个页面请求通过一个unix domain socket(都位于同一物理服务器)或者一个IP Socket(FastCGI部署在其它物理服务器)传递给FastCGI进程。
step1. Web 服务器启动时载入初始化FastCGI执行环境 。 例如IIS ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi
step2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web 服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。
step3. 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发到 FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
step4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。
step5. FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。
由于 FastCGI 程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。
它的速度效率最少要比CGI 技术提高 5 倍以上。
它还支持分布式的部署, 即 FastCGI 程序可以在web 服务器以外的主机上执行。
总结:CGI 就是所谓的短生存期应用程序,FastCGI 就是所谓的长生存期应用程序。
FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。
https://fastcgi-archives.github.io/
fastcgi的子进程和普通的cgi程序应该会有些区别的吧
确实有点不一样,从 c 语言的角度来看,cgi 只要能处理 环境变量 标准输入 标准输出 标准错误 就可以了,基本和普通的程序没区别
但 fastcgi 需要引入一个 fascig_stdio 的头文件
fascgi 的开发工具
https://github.com/FastCGI-Archives/fcgi2
https://fastcgi-archives.github.io/
fastcgi 分成两部分
fastcgi 管理器 或者叫做 fascgi 主进程
fastcgi 子进程
fpm 相当于 fastcgi 管理器
php-cgi 相当于 fastcgi 子进程
http 服务器是可以直接和 fastcgi 子进程 通讯的
Spawn-fcgi 是一个 fastcgi 管理器,可以运行包括 php 在内的其它 fastcgi 程序,特别是那种用 c 写的 fastcgi
fascgi 转换 cgi
https://github.com/gnosek/fcgiwrap
https://man.archlinux.org/man/fcgiwrap.8.en
https://man.freebsd.org/cgi/man.cgi?query=fcgiwrap&sektion=8&manpath=freebsd-ports
https://pkgs.alpinelinux.org/package/edge/main/x86/fcgiwrap
https://redmine.lighttpd.net/projects/fcgi-cgi 这个可以和 spawn-fcgi 配合使用
https://github.com/dermesser/fastcgi-wrappers
https://github.com/FastCGI-Archives/fcgi2/blob/master/cgi-fcgi/cgi-fcgi.c
那么 cgi 怎么转换 fastcgi 。。。?
https://htmlpreview.github.io/?https://raw.githubusercontent.com/FastCGI-Archives/fcgi2/master/doc/fastcgi-prog-guide/cover.htm
fastcgi 的白皮书
https://fastcgi-archives.github.io/FastCGI_A_High-Performance_Web_Server_Interface_FastCGI.html
FastCGI 实现基本上在两个没有关系的进程之间创建双向连接.
fastcgi 和 http 是以fastcgi协议通讯的
http 会把输入和环境变量一起发给 fastcgi
fastcgi 会把输出经过封装后发给 http
fcgi_stdio Library
能兼容 cgi 和 fastcgi
fastcgi 协议的文档
https://fastcgi-archives.github.io/FastCGI_Specification.html
用 php 实现 fascgi 的库
https://github.com/lisachenko/protocol-fcgi
cgi 的标准文档
RFC 3875 - The Common Gateway Interface (CGI) Version 1.1
本质上就是一个守护进程按着 fascgi 协议读写 socket (可以是 网络socket 也可以是 unix socket)
fastcgi 协议应该是分成两部分的
一部分是和 http 通讯的
一部分是和 fastcgi 子程序通讯的
也有可能是这样的 fastcgi 的管理器只是转发内容,实际上 fastcgi 的子程序可以直接跟 http 通讯的,如果是这样的,那么也就解释了为什么 php-cgi 能直接和 nginx 通讯了
https://blog.csdn.net/tanswer_/article/details/78879905
https://blog.csdn.net/shreck66/article/details/50355729
https://xiaoxia.org/2009/10/05/fastcgi-protocol-analysis/
https://www.mit.edu/~yandros/doc/specs/fcgi-spec.html
https://www.infoq.cn/article/vIcwtItzVK7B4YNOEj3e
有空研究一下 spawn-fcgi 的代码
spawn-fcgi代码不到600行,非常简短精炼
https://www.cnblogs.com/redsmith/p/5467885.html
https://redmine.lighttpd.net/projects/spawn-fcgi
要理解到 php-cgi 的工作模式,最好还是去看源码
fpm 的全称是 php-fpm ,虽然大部分语境下 fpm 就是指 php-fpm
fpm
FastCGI Process Manager
RFC(Request For Comments)意即“请求评论”,包含了关于Internet的几乎所有重要的文字资料。
fastcgi 没有 rfc
伯克利套接字(英语:Internet Berkeley sockets) ,又称为BSD 套接字(BSD sockets)是一种应用程序接口(API),用于网络套接字( socket)与Unix域套接字
Berkeley套接字应用程序接口形成了事实上的网络套接字的标准精髓。 大多数其他的编程语言使用与这套用C语言写成的应用程序接口[1] 类似的接口。
这套应用程序接口也被用于Unix域套接字(Unix domain sockets),后者可以在单机上为进程间通讯(IPC)的接口。
cgi
输入
环境变量
标准输入
输出
标准输出
标准错误
fcgi
输入
socket连接
环境变量
标准输入
输出
socket连接
标准输出
标准错误
实际上除了 php 之外,其它编程语言很少用 fastcgi ,虽然主流的编程语言都有 fastcgi 的实现
fcgistarter
apache 2.4 之后就有的 fcgi 工具
只支持 linux
php解释器在 cgi-bin 目录?
把php-cgi复制到 cgi-bin 目录
用软连接
php-cgi-wrapper ?
#!/bin/sh
exec /usr/bin/php-cgi
php解释器不在 cgi-bin 目录?
使用别名的方式把 cgi-bin 目录指向 php-cgi 的目录
ScriptAlias "/cgi-bin/" "/usr/local/apache2/cgi-bin/"
使用 apache action ?
需要 mod_actions 模块
使用 使用以 #! 开头的 shell-escape 机制来启动 php ?
cgi的三种方式?
普通的cgi ,只依赖 cgi 模块
php解释器在 cgi-bin 目录 (这种算是执行cgi程序)
使用以 #! 开头的 shell-escape 机制来启动 php (这种算是执行cgi脚本)
php-cgi-wrapper是先执行脚本再执行程序,也算是执行cgi脚本
使用 apache action
apache action 依赖 cgi 模块 和 action 模块
apache 独有的
php-cgi 是如何区分当前是 cgi模式 还是 fastcgi模式?
如果是 nginx 或 mod_proxy_fcgi 的情况下可以通过 -b 参数来区分
但如果是 apache mod_fcgid 模块运行的,那是如何区分的?
在源码里,有一个 fastcgi变量用于判断当前是否处于 fastcgi
int fastcgi;
....
fastcgi = fcgi_is_fastcgi();
fcgi_is_fastcgi
fcgi_init
getpeername(0, (struct sockaddr *)&sa, &len) != 0
FastCGI 管理进程通过 fork()+execve()创建 FastCGI 程序子进程,然后使用 unix socket 与子进程通讯
sapi/cgi/cgi_main.c
main/fastcgi.c
errno = 0;
if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
fcgi_setup_signals();
return is_fastcgi = 1;
} else {
return is_fastcgi = 0;
}
ENOTCONN 是 POSIX/Unix 系统调用中表示的错误码,含义是“套接字没有连接”(Socket is not connected)
这段代码的意思是
如果 套接字存在但还没有连接 那么就是 fastcgi 状态,否则就是 cgi 状态
如果是 cgi 状态运行这句 getpeername(0, (struct sockaddr *)&sa, &len) != 0
errno 的值是 ENOTSOCK
ENOTSOCK 是 POSIX/Unix 系统错误码,表示“不是套接字”(Not a socket)
cgi 本质上也是命令行里运行
export REQUEST_METHOD=GET
export SCRIPT_FILENAME=/var/www/html/index.php
export QUERY_STRING="a=1&b=2"
export CONTENT_LENGTH=0
export SERVER_PROTOCOL=HTTP/1.1
export SERVER_NAME=localhost
export SERVER_PORT=80
export DOCUMENT_ROOT=/var/www/html
/usr/bin/php-cgi
CGI模式
PHP 在 Apache 2中的 CGI模式。编辑Apache 配置文件httpd.conf 如下:
# PHP4 版写法
ScriptAlias /php/ "D:/php/"
AddType application/x-httpd-php .php
Action application/x-httpd-php "/php/php.exe"
# PHP5 版写法
ScriptAlias /php/ "D:/php/"
AddType application/x-httpd-php .php
Action application/x-httpd-php "/php/php-cgi.exe"
cgi 和 PATH_INFO环境变量
/cgi-bin/php-cgi/sapi.php
cgi 的 rfc3875
标准里有规定是需要传递 哪些环境变量
nginx 对 cgi 的支持并不完善
手动设置环境并运行 php-cgi
# GET 请求模拟
REQUEST_METHOD=GET \
SCRIPT_FILENAME=/path/to/test.php \
QUERY_STRING='name=foo' \
php-cgi
# POST 请求模拟
REQUEST_METHOD=POST \
SCRIPT_FILENAME=/path/to/test.php \
CONTENT_TYPE='application/x-www-form-urlencoded' \
CONTENT_LENGTH=11 \
php-cgi <<EOF
name=alice
EOF
cgi.force_redirect = 1
必须通过 web server 执行 cgi
如果是在命令行里模拟,那么这个参数要设为 0
php 是怎么判断 从 web web server 执行 cgi 还是从 命令行 执行 cgi ?
php-cgi -d cgi.force_redirect=0
php-cgi -d cgi.force_redirect=0 -i | grep "Server API"
php -i | grep "Server API"
echo "<?php echo php_sapi_name();echo PHP_EOL;" | php
echo "<?php echo php_sapi_name();echo PHP_EOL;" | php-cgiecho "<?php echo php_sapi_name();echo PHP_EOL;" | php-cgi -d cgi.force_redirect=0
echo "<?php echo php_sapi_name();echo PHP_EOL;" > sapi.php
这一段是有效果的,但 sapi name 依然是 cgi-fcgi
export REQUEST_METHOD=GET
export SCRIPT_FILENAME=/c/Users/81522963/dev/stocks/sapi.php
export QUERY_STRING="a=1&b=2"
export CONTENT_LENGTH=0
export SERVER_PROTOCOL=HTTP/1.0
export SERVER_NAME=localhost
export SERVER_PORT=80
export DOCUMENT_ROOT=/c/Users/81522963/dev/stocks
php-cgi
这个配置示例展示了 Web 服务器架构的历史演变,理解它有助于维护遗留系统,
但新项目应优先考虑 PHP-FPM 或容器化部署方案。
在 2025 年,PHP-CGI 主要价值在于特定兼容性需求或教育目的。
php-cgi 本身支持 -b 参数监听 FastCGI socket 或端口:
# 启动 php-cgi 监听 127.0.0.1:9000(FastCGI 模式)
php-cgi -b 127.0.0.1:9000
# 或使用 Unix socket(更高效)
php-cgi -b /tmp/php-cgi.sock
在 Nginx 配置中:
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # 或 fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
include 指令:在 nginx 配置文件里用于包含(引入)另一个文件或一组文件
include fastcgi_params; 是把一组预定义的 FastCGI 参数引入当前配置,使 nginx 在转发到 FastCGI 后端时传递这些参数。
cgi 一开始就有 1995
apache模块 1997 1998
首次提供 Apache 模块支持(需手动编译 mod_php2)
真正成熟且广泛采用的 Apache 模块支持始于 PHP 3.0
php-cgi 4.0 2000
Version 4.0.5 Added FastCGI SAPI module
cli 4.3 php-cli 2002
fpm 5.3开始捆版发行 2009,大概在 5.0 的时候出现 2005
PHP-FPM(PHP FastCGI Process Manager)是在
2004年 由Andrei Nigmatulin发明的,它是一种专用于管理PHP FastCGI进程的软件。
4.0 之前没有找到 更新日志
pdo 5.1
原生的JSON支持 5.2
命名空间 5.3
方括号的数组 5.4
PHP/FI
Personal Home Page / Forms Interpreter
从 php3 开始 就改名了, PHP: Hypertext Preprocessor ,这是一个递归的缩写
PHP/FI 1
PHP/FI 2
PHP 3
composer 1.0 2012
最低要求: PHP 5.3.2
5.4、5.5、5.6
不推荐 7 之后继续用 composer 1 ,虽然也能用
特性出现 捆绑在官方发行版里
Lighttpd
spawn-fcgi
这两个依然有保持更新
在 fpm 出现之前还有一个 spawn-fcgi
spawn-fcgi 来自 Lighttpd
Lighttpd 本身也支持直接管理 fastcgi 进程,但功能有限
spawn-fcgi 需要编译安装,支持 linux和windows
cgi(直到 PHP 5.3)
https://www.php.net/manual/zh/function.php-sapi-name.php
由某些打包/定制的 Apache+PHP 二进制组合编译时,SAPI 名称被设为 "apache"(历史或平台差异),
但现代 Apache2 通常显示 "apache2handler"。
DNSSEC
http ipv4
http ipv6
https ipv4
https ipv6
h2 ipv4
h2 ipv6
h3 ipv4
h3 ipv6
-->