仓库源文站点原文

计算机网络基础知识

关键的概念

什么是计算机网络

计算机网络连接是指连接两台或多台计算设备(如台式计算机、移动设备、路由器或应用程序)以实现信息与资源的传输和交换的流程。

计算机网络的分类

现在最常用的 以太网 是物理上使用星形拓扑(有一个路由器或集线器作为中心), 逻辑上使用总线拓扑(使用CSMA/CD的总线技术)

计算机网络的模型

数据链路层

载波侦听多路访问

路由表和路由算法

网际层

IP

ARP

IPsec

传输层

TCP

UDP

ICMP

应用层

代理 网关 隧道 的区别,还有 VPN 和 端口转发

    代理 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 -->