当前位置: 首页 > news >正文

项目 【Http】第二讲---CGI机制的介绍

前言:

在上一讲中,我们系统性地回顾了网络通信的基础知识,从TCP/IP四层模型到HTTP协议的请求-响应流程,为后续的深入学习打下了坚实的理论基础。然而,静态的网页内容仅仅是网络世界的冰山一角,真正的动态交互能力才是现代Web应用的核心。

本讲我们将聚焦于CGI(通用网关接口)这一关键机制。作为早期实现动态网页的核心技术,CGI通过定义服务器与外部程序的标准交互方式,使开发者能够利用脚本语言(如Python、Perl)或编译型程序生成动态内容。尽管如今涌现了FastCGI、WSGI等优化方案,理解CGI的原理依然是掌握HTTP服务端开发的重要基石。通过本讲,你将亲自动手实现一个简易的CGI流程,直观感受服务器如何将请求参数传递给外部程序,并将结果动态返回给客户端。


一、CGI机制的概念

CGI(Common Gateway Interface,通用网关接口)是一种C++当今仍在使用的互联网技术。

CGI 即 Common Gateway Interface,译作“通用网关接口”。初次听闻,略感疑惑,实则每个字眼都值得玩味。

1. Common

通用,是一个显著特征。虽然我们听说过Java的Servlet,Python的WSGI。但其实Java、Python都是支持CGI的,不仅如此,其他我们所熟知的语言大都也都支持。理论上来说,所有支持标准输出,支持获取环境变量的编程语言都能用来编写CGI程序。

这是它通用性的体现,我们的CGI程序不限任何语言。

2.Gateway

Gateway,网关。各位看官,不用好奇,不用疑惑。随便打开一本HTTP的书,都会提到网关的概念。通常意识中,网关一词更多的是硬件层面的概念,但其实与CGI的网关二字之含义也是不谋而合的。称CGI为软件网关也不为过。

3. Interface

Interface:Come On。又是一个被国人翻译烂掉的词汇。API(应用程序接口)的I是它,被译作“接口”。UI(用户界面)的I也是它,被译作“界面”。实际他们是同样的意思。我更喜欢“接口”一词。

CGI描述了服务器和请求处理程序之间传输数据的一种标准。这是一种常见的动态处理浏览器请求的方法。

实际我们在进行网络请求时,无非就两种情况:

  • 浏览器想从服务器上拿下来某种资源,比如打开网页、下载等。
  • 浏览器想将自己的数据上传至服务器,比如上传视频、登录、注册等。

这两种情况在HTTP当中对应的方法就是GET / POST。

通常从服务器上获取资源对应的请求方法就是GET方法,而将数据上传至服务器对应的请求方法就是POST方法,但实际GET方法有时也会用于上传数据,只不过POST方法是通过请求正文传参的,而GET方法是通过URL传参的。

  而用户将自己的数据上传至服务器并不仅仅是为了上传,用户上传数据的目的是为了让HTTP或相关程序对该数据进行处理,比如用户提交的是搜索关键字,那么服务器就需要在后端进行搜索,然后将搜索结果返回给浏览器,再由浏览器对HTML文件进行渲染刷新展示给用户。

  但实际对数据的处理与HTTP的关系并不大,毕竟HTTP只是一个传输数据的协议与底层的TCP/IP协议有相似之处,具体的数据处理是取决于上层具体的业务场景的,因此HTTP不对这些数据做处理。

但HTTP提供了CGI机制,上层可以在服务器中部署若干个CGI程序,这些CGI程序可以用任何程序设计语言编写,当HTTP获取到数据后会将其提交给对应CGI程序进行处理,然后再用CGI程序的处理结果构建HTTP响应返回给浏览器。

  其中HTTP获取到数据后,如何调用目标CGI程序、如何传递数据给CGI程序、如何拿到CGI程序的处理结果,这些都属于CGI机制的通信细节,而本项目就是要实现一个HTTP服务器,因此CGI的所有交互细节都需要由我们来完成。 


二、CGI机制的实现步骤

关于谈到具体的实现步骤,我们必须明白CGI的工作流程,抛开上面的话在我看来,CGI技术看起来名词多高大上,什么网关,什么通用之类。

但是在我看来就是一个实现当用户访问网页程序的时候,正确的将数据拿到执行程序那,然后把程序结果返回给用户。

既然有了对于CGI的认识,下面想一下我们可能遇到的困难。

2.1、在HTTP协议当中我们怎么判断用户要访问的内容是不是执行CGI程序。

很简单,HTTP报头不是包含了用户的访问资源路径吗?

我们可以根据路径对于的是不是可执行程序来判断,是说明用户访问的是一个可执行程序我们进入CGI流程。

2.2、怎么调用CGI程序呢?

在前面我们介绍CGI的时候,发现其实用户访问的资源如果进程CGI流程当中,那么我们是不是就要调用访问的CGI程序了,但是该怎么调用呢?

我们有了想法,在Linux的系统编程当中我们学习过execl系列函数,这批函数都是进行程序替换的,当用户访问CGI程序的时候,我们创建一个进程,然后替换调它不就好了。

2.3 CGI程序进程怎么通信呢?

那好这又回到我们一开始讲的,用户访问一个CGI程序大概率是将某些参数也是一起提交上来的,可是我们又明白程序替换,是将数据和代码一起替换了,如果是这样那么CGI程序拿不到用户的数据,我们也拿不到CGI的处理结果。

所以我们是一定要解决,CGI与我们HTTP的进程间通信的问题。

我们应该选择什么方式呢?

选择管道与环境变量,为啥这两种方式够简单,况且CGI进程是被我们的HTTP处理请求的进程创建的子进程,它们具有父子关系,为啥不用环境变量呢?

但是使用环境变量加管道也是存在使用问题的,比如我们的管道是单向的,可按照我们的设想,CGI进程与我们的HTTP进程是可以进行双向通信的,那我们怎么办?

直接创建两个管道不就好了,但是如果是为了双向通信,还是要关掉一些没用的管道方向的,对于HTTP进程与CGI进程。

2.4 管道问题

但是问题还没有解决,那就是上面我们提到了,程序替换后进程的源数据就丢失了,那我们的CGI程序还是拿不到管道打开的文件描述符啊,所以我们怎么办,难道把打开的文件描述符也当成一个环境变量,让CGI进程继承下去,好像不是不可以,但是我们有更好的解决方式。

我们可以在CGI程序进程与HTTP进程之间,做个约定,我们人为的规定CG程序的标准输入与标准输出进行重定向,将两个管道打开的文件描述符重定向到标准输入与标准输出。HTTP要写数据直接向CGI程序进程的标准输入当中写,GCI要读就从标准输入里写。

这样即使进程替换后,CGI程序仍然可以使用管道。

2.5 正式的父子进程交付数据

这时父子进程已经能够通过两个匿名管道进行通信了,接下来就应该讨论父进程如何将数据交给CGI程序,以及CGI程序如何将数据处理结果交给父进程了。

父进程将数据交给CGI程序:

如果请求方法为GET方法,那么用户是通过URL传递参数的,此时可以在子进程进行进程程序替换之前,通过putenv函数将参数导入环境变量,由于环境变量也不受进程程序替换的影响,因此被替换后的CGI程序就可以通过getenv函数来获取对应的参数。
如果请求方法为POST方法,那么用户是通过请求正文传参的,此时父进程直接将请求正文中的数据写入管道传递给CGI程序即可,但是为了让CGI程序知道应该从管道读取多少个参数,父进程还需要通过putenv函数将请求正文的长度导入环境变量。
说明一下: 请求正文长度、URL传递的参数以及请求方法都比较短,通过写入管道来传递会导致效率降低,因此选择通过导入环境变量的方式来传递。

  也就是说,使用CGI模式时如果请求方法为POST方法,那么CGI程序需要从管道读取父进程传递过来的数据,如果请求方法为GET方法,那么CGI程序需要从环境变量中获取父进程传递过来的数据。

  但被替换后的CGI程序实际并不知道本次HTTP请求所对应的请求方法,因此在子进程在进行进程程序替换之前,还需要通过putenv函数将本次HTTP请求所对应的请求方法也导入环境变量。因此CGI程序启动后,首先需要先通过环境变量得知本次HTTP请求所对应的请求方法,然后再根据请求方法对应从管道或环境变量中获取父进程传递过来的数据。

  CGI程序读取到父进程传递过来的数据后,就可以进行对应的数据处理了,最终将数据处理结果写入到管道中,此时父进程就可以从管道中读取CGI程序的处理结果了。


三、CGI机制的意义

CGI机制就是让服务器将获取到的数据交给对应的CGI程序进行处理,然后将CGI程序的处理结果返回给客户端,这显然让服务器逻辑和业务逻辑进行了解耦,让服务器和业务程序可以各司其职。


CGI机制使得浏览器输入的数据最终交给了CGI程序,而CGI程序输出的结果最终交给了浏览器。这也就意味着CGI程序的开发者,可以完全忽略中间服务器的处理逻辑,相当于CGI程序从标准输入就能读取到浏览器输入的内容,CGI程序写入标准输出的数据最终就能输出到浏览器。

处理HTTP请求的步骤如下:

  1. 判断请求方法是GET方法还是POST方法,如果是GET方法带参或POST方法则进行CGI处理,如果是GET方法不带参则进行非CGI处理。
  2. 非CGI处理就是直接根据用户请求的资源构建HTTP响应返回给浏览器。
  3. CGI处理就是通过创建子进程进行程序替换的方式来调用CGI程序,通过创建匿名管道、重定向、导入环境变量的方式来与CGI程序进行数据通信,最终根据CGI程序的处理结果构建HTTP响应返回给浏览器。
     


总结:

至此,我们完成了对CGI机制的探讨。通过本讲的学习,我们掌握了CGI如何作为“桥梁”连接Web服务器与外部程序,实现动态内容的生成与交互。这一机制不仅是理解现代Web框架(如FastCGI、WSGI)的基础,也为后续功能扩展提供了思路。

在下一讲《项目【Http】第三讲---日志板块的编写》中,我们将聚焦服务器开发中至关重要的日志模块。日志是服务器监控、调试和优化的“眼睛”,其设计需要兼顾高效性、灵活性与可读性。我们将从日志分级、格式化输出等角度展开,逐步实现一个轻量且实用的日志系统。敬请期待!

http://www.xdnf.cn/news/1038925.html

相关文章:

  • 【Unity】使用 C# SerialPort 进行串口通信
  • Java 常用类 Math:从“如何生成随机密码”讲起
  • LCEL:LangChain 表达式语言详解与测试工程师的实践指南
  • 钉钉机器人-自定义卡片推送快速入门
  • wget批量调用shell脚本
  • C#里与嵌入式系统W5500网络通讯(7)
  • 有关Spring事务的传播机制
  • 23. [实用] 扣子(coze)教程 | 小程序UI设计进阶(五)只此一家,标签组件攻略
  • 互联网校招腾讯26届校招暑期实习综合素质测评答题攻略及真题题库
  • monorepo使用指北
  • 123数字人视频剪辑源码搭建部署/数字人视频创作技术开发
  • React配置别名路径完整指南
  • uniapp的app项目,在华为pad上运行,页面显示异常
  • 动目标显示处理解析六(重频参差扩展盲速)
  • static的三种作用
  • 【C++】模拟实现map和set
  • [Linux入门] Linux磁盘管理与文件系统
  • YOLOv3 中的 IoU 计算详解
  • 在Ubuntu linux终端写文件的方法
  • FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
  • 【Zephyr 系列 25】多芯片协同设计:主控 + BLE + LoRa 芯片的统一调度与消息系统
  • 什么是泛型,如何使用它?
  • 动态组件(component)的高级使用
  • PL端DDR3读写(1)
  • 转换专家从格式转换到创意剪辑的全链路解决方案
  • AIGC 基础篇 Python基础(练习1)
  • 板凳-------Mysql cookbook学习 (十--6)
  • Python6.14打卡(day46)
  • StampedLock入门教程
  • 面试问题总结——关于C++(四)