目录

  • fastCGI
    • 1. CGI
      • 1.1 简介
      • 1.2 CGI处理流程
      • 1.3 环境变量
      • 1.4 标准输入
      • 1.5 CGI程序结构
      • 1.6 测试
    • 2. FastCGI
      • 2.1 什么是FastCGI
      • 2.2 FastCGI处理流程
      • 2.3 FastCGI程序结构
      • 2.4 fastCGI的环境变量 - fastcgi.conf
    • 3. 组合使用nginx,fastcgi,spawn-fcgi
      • 3.1 fastCGI和spawn-fcgi的安装
      • 3.2 nginx,fastcgi,fastcgi进程管理器spawn-fcgi 三者之间的关系
      • 3.3 演示1 nginx&&fastcgi
      • 3.4 演示2:nginx,使用fcgi库编写程序,使用spawn-fcgi启动fcgi程序
      • 3.5 fastcgi程序的开发技巧小结
      • 3.6 演示3:客户端(浏览器)向服务器发送了一个url:
    • 4. 补充知识点
      • 1. 客户端使用Post方式提交数据
        • 消息主体的常用格式
      • 2. strtol 函数
    • 5. fastCGI复习
    • 6 实战
      • 1. 配置静态网页register.html
      • 2. 配置动态网页(完成注册页面) register
        • register.html(同6.1)
        • user_opt.h
        • user_opt.cpp
          • add_user.sh
          • check_user.sh
        • register_fcgi.cpp
        • makefile
        • 运行
      • 整体流程图!!!!!!
      • 6.3 配置动态网页(完成登录页面) login
        • register.html(同6.1)
        • user_opt.h
        • user_opt.cpp
          • check_user.sh
        • login_fastcgi.cpp
        • makefile
        • 运行

fastCGI

1. CGI

1.1 简介

通用网关接口(Common Gateway Interface、CGI)描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。

CGI独立于任何语言的,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix shell script、Python、 Ruby、PHP、 perl、Tcl、 C/C++和 Visual Basic 都可以用来编写 CGI 程序。

最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务
器开发的。这个 Web 服务器使用了 UNIX shell 环境变量来保存从 Web 服务器传递出去的参数,然后生成一个运行 CGI 的独立的进程。

1.2 CGI处理流程

  1. web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输入传递数据
  2. CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
  3. CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
  4. web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程

CGI 运行流程 图


web服务器与CGI通过环境变量、标准输入、标准输出、标准错误互相传递数据。在遇到用户连接请求:

  • 先要创建CGI子进程,然后CGI子进程处理请求,处理完事退出这个子进程:fork-and-execute
  • CGI方式是客户端有多少个请求,就开辟多少个子进程,每个子进程都需要启动自己的解释器、加载配置,连接其他服务器等初始化工作,这是CGI进程性能低下的主要原因。当用户请求非常多的时候,会占用大量的内存、cpu等资源,造成性能低下。

输入:读取环境变量、标准输入
输出:标准输出、标准错误

CGI使外部程序与Web服务器之间交互成为可能。CGI程序运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。

1.3 环境变量

CGI程序通过getenv()函数获取环境变量中的值

GET请求,它将数据打包放置在环境变量QUERY_STRING中,CGI从环境变量
QUERY_STRING中获取数据。

常见的环境变量如下表所示:

环境变量 含义
AUTH_TYPE 存取认证类型
CONTENT_LENGTH 由标准输入传递给CGI程序的数据长度,以bytes或字元数来计算
CONTENT_TYPE 请求的MIME类型
GATEWAY_INTERFACE 服务器的CGI版本编号
HTTP_ACCEPT 浏览器能直接接收的Content-types, 可以有HTTP Accept header定义
HTTP_USER_AGENT 递交表单的浏览器的名称、版本和其他平台性的附加信息
HTTP_REFERER 递交表单的文本的URL,不是所有的浏览器都发出这个信息,不要依赖它
PATH_INFO 传递给CGI程序的路径信息
QUERY_STRING 传递给CGI程序的请求参数,也就是用"?"隔开,添加在URL后面的字串
REMOTE_ADDR client端的host名称
REMOTE_HOST client端的IP位址
REMOTE_USER client端送出来的使用者名称
REMOTE_METHOD client端发出请求的方法(如get、post)
SCRIPT_NAME CGI程序所在的虚拟路径,如/cgi-bin/echo
SERVER_NAME server的host名称或IP地址
SERVER_PORT 收到request的server端口
SERVER_PROTOCOL 所使用的通讯协定和版本编号
SERVER_SOFTWARE server程序的名称和版本

1.4 标准输入

环境变量的大小是有一定的限制的,当需要传送的数据量大时,储存环境变量的空间可能会不足,造成数据接收不完全,甚至无法执行CGI程序。

因此后来又发展出另外一种方法:POST,也就是利用I/O重新导向的技巧,让CGI程序可以由stdin和stdout直接跟浏览器沟通。

当我们指定用这种方法传递请求的数据时,web服务器收到数据后会先放在一块输入缓冲区
中,并且将数据的大小记录在CONTENT_LENGTH这个环境变量,然后调用CGI程序并将
CGI程序的stdin指向这块缓冲区,于是我们就可以很顺利的通过stdin和环境变数CONTENT_LENGTH得到所有的信息,再没有信息大小的限制了。

1.5 CGI程序结构

CGI程序的执行流程

CGI程序起始于 被Web程序拉起来
先读取环境变量(getenv(),获取HTTP请求头部分)
从标准输入读数据(fgets() fread() ,获取请求体)
数据处理 // 不同的CGI程序,只有这部分不一样(具体的业务处理)
向标准输出输出数据(用HTML格式组织好 printf()输出)
CGI程序退出


CGI程序的缺点:资源消耗大;某些初始化操作冗余;CGI进程会在服务器端被频繁的创建、销毁 ,降低了服务器端的处理效率



客户端通过浏览器向服务器发送一个登陆请求 http://localhost/login?user=zhang3&passwd=123456

  1. 客户端给服务器发送http请求, 要求登陆
  2. 服务器接收客户端请求, 服务器无法处理登陆操作
    • 服务器要处理请求, 先创建一个子进程, 目的的帮助服务器解析服务器无法解析的数据

      • Cgi程序是由程序员写好之后提供给web服务器的
    • cgi程序先进行初始化
      • 连接数据库
    • web服务器将要处理的数据发送给cgi程序
      • cgi程序进行数据库查询
      • 将查询结果发送给web服务器
      • 服务器将CGI进程销毁
    • web服务器将结果发送给客户端
  3. 使用CGI结论:
    • 如果使用CGI解析服务器要处理的动态数据

      • CGI进程会在服务器端被频繁的创建、销毁 ;降低了服务器端的处理效率

1.6 测试

CGI程序通过getenv()函数获取环境变量中的值


#include <stdio.h>
#include <stdlib.h>
int main()
{puts(getenv("APPLE"));//通过读取环境变量里的值,来获取收到的数据return 0;
}[root@lwh testcpp]# APPLE=123456 ./execute # 我在运行这个程序之前直接设置环境变量(运行结束,这个环境变量就失效)
123456
[root@lwh testcpp]# APPLE=hhhh ./execute
hhhh
[root@lwh testcpp]# ./execute
Segmentation fault (core dumped)
[root@lwh testcpp]#
#include <stdio.h>
#include <stdlib.h>
int main()
{char buff[128] = {0};fgets(buff, sizeof(buff), stdin); //从标准输入获取收到的数据puts(buff);puts(getenv("APPLE")); //通过读取环境变量里的值,来获取收到的数据return 0;
}# echo abcd | ./execute 是通过‘|’把echo程序的标准输出的数据传递给了./execute的标准输入
# APPLE=jhjhjh          是临时设置这个环境变量的值
[root@lwh testcpp]# echo abcd | APPLE=jhjhjh ./execute
abcdjhjhjh
[root@lwh testcpp]#

2. FastCGI

2.1 什么是FastCGI

快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。

FastCGI致力于减少Web服务器与CGI程式之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。

CGI与FastCGI的关系:CGI 就是所谓的短生存期应用程序,FastCGI 就是所谓的长生存期应用程序。FastCGI像是一个常驻(longlive)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次

2.2 FastCGI处理流程

  1. Web 服务器启动时载入初始化FastCGI执行环境。 例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
  2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。
  3. 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发给FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
  4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。
  5. FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。

fastcgi执行流程

  1. 客户端向服务器端发送登录请求

  2. 服务器收到登录请求无法处理, 服务器端启动fastCGI进程管理器

  3. 在fastCGI进管理器中管理了一个CGI程序

    • 进入循环的意思?

      • 循环处理服务器发送的请求
      • 如果服务器没有发送请求消息, 循环阻塞
  4. fastCGI处理完毕之后, 将结果发送给web服务器

  5. web服务器将结果发送给客户端

由于FastCGI程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。它还支持分布式的部署,即FastCGI 程序可以在web 服务器以外的主机上执行。

CGI 是所谓的短生存期应用程序,FastCGI 是所谓的长生存期应用程序。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。

2.3 FastCGI程序结构

FastCGI程序流程图



2.4 fastCGI的环境变量 - fastcgi.conf

环境变量 说明
SCRIPT_FILENAME 脚本文件请求的路径
QUERY_STRING 请求的参数;如?app=123
REQUEST_METHOD 请求的动作(GET,POST)
CONTENT_TYPE 请求头中的Content-Type字段
CONTENT_LENGTH 请求头中的Content-length字段
SCRIPT_NAME 脚本名称
REQUEST_URI 请求的地址不带参数
DOCUMENT_URI 与$uri相同
DOCUMENT_ROOT 网站的根目录。在server配置中root指令中指定的值
SERVER_PROTOCOL 请求使用的协议,通常是HTTP/1.0或HTTP/1.1
GATEWAY_INTERFACE cgi 版本
SERVER_SOFTWARE nginx 版本号,可修改、隐藏
REMOTE_ADDR 客户端IP
REMOTE_PORT 客户端端口
SERVER_ADDR 服务器IP地址
SERVER_PORT 服务器端口
SERVER_NAME 服务器名,域名在server配置中指定的server_name


3. 组合使用nginx,fastcgi,spawn-fcgi

3.1 fastCGI和spawn-fcgi的安装

1.安装fastCGI

tar -zxvf fcgi-2.4.1-SNAP-0910052249.tar.gz
ls
cd fcgi-2.4.1-SNAP-0910052249/
ls
./configure      # --prefix=/usr  //安装到/usr目录下
make             # make -j 2 两个线程同时make,快一些# - error: fcgio.cpp:50:14: error: 'EOF' was not declared in this scope# - 解决方案: 添加声明 EOF 的头文件到fcgio.cpp #      #include <stdio.h> ->   c #        #include <cstdio>  -> C++
make             # 重新执行make
sudo make install
# make和执行时需要加载动态库:libfcgi.so
[root@lwh lib]# pwd
/usr/local/lib
[root@lwh lib]# vim /etc/ld.so.conf # 更改一下配置 添加库路径
[root@lwh lib]# ldconfig            # 然后生效一下

2.安装spawn-fcgi

下载地址: http://redmine.lighttpd.net/projects/spawn-fcgi/wiki 安装

./configure       # --prefix=/usr  //安装到/usr目录下
make
sudo make install[root@lwh spawn-fcgi-1.6.4]# whereis spawn-fcgi
spawn-fcgi: /usr/local/bin/spawn-fcgi


3.2 nginx,fastcgi,fastcgi进程管理器spawn-fcgi 三者之间的关系

nginx 不能像apache那样直接执行外部可执行程序,但nginx可以作为代理服务器,将请求转发给后端服务 器,这也是nginx的主要作用之一。其中nginx就支持FastCGI代理,接收客户端的请求,然后将请求转发给后 端fastcgi进程。下面介绍如何使用C/C++编写cgi/fastcgi,并部署到nginx中。

通过前面的介绍知道,fastcgi进程由FastCGI进程管理器管理,而不是nginx。这样就需要一个FastCGI管理, 管理我们编写fastcgi程序。我们使用spawn-fcgi作为FastCGI进程管理器。

spawn-fcgi是一个通用的FastCGI进程管理器,简单小巧,原先是属于lighttpd的一部分,后来由于使用比较 广泛,所以就迁移出来作为独立项目了。spawn-fcgi使用pre-fork 模型,功能主要是打开监听端口,绑定地 址,然后fork-and-exec创建我们编写的fastcgi应用程序进程,退出完成工作。fastcgi应用程序初始化,然后 进入死循环侦听socket的连接请求。

  • 进程管理器启动的时候

    • 会创建fastCGI进程
    • 因此: 进程管理器和fastCGI进程是父子关系
  • 进程管理器启动的时候
    • 需要指定监听的端口和IP地址, 并对其绑定

      • 因此:进程管理器可以接收nginx发送的数据
      • 然后进程管理器会将数据发送给fastCGI进程
  • Nginx处理不了的动态数据, 会转发, 转发给fastCGI进程
  • fastCGI进程处理Nginx发送的数据, 得到结果
  • fastCGI进程将结果发送给Nginx
  • Nginx将处理结果发送给客户端


3.3 演示1 nginx&&fastcgi

# 更改配置文件,添加fastCGI的支持
[root@lwh testcpp]# vim /usr/local/nginx/conf/nginx.conflocation /fast/{include fastcgi_params;fastcgi_pass 192.168.140.1:55555;}
# 重新加载配置文件
[root@lwh conf]# nginx -s reload
# 在浏览器访问:192.168.184.134/fast/  # 192.168.184.134是虚拟机的IP nginx默认监听80端口# nginx找到 /fast/ 的location# nginx收到了,转发到fastcgi_pass 192.168.140.1:55555;# 本机(WIndows)上的TCP socket工具设置了监听192.168.140.1:55555# nginx转发的数据到达后,就会显示到接收栏

此时socket工具收到转发过来的数据(从浏览器封装成HTTP数据–>到nginx的web服务器–>转换成fastCGI格式数据–>转到本IP和Port)

fastCGI只启动了一个进程,通过socket接收和回复web服务器发送来的数据
数据是基于fastCGI标准编码过的

3.4 演示2:nginx,使用fcgi库编写程序,使用spawn-fcgi启动fcgi程序


由文章第1 部分和文章第二部分的配图 可以看出,fast-cgi程序和cgi程序的相似度很大,但又不完全相同。fcgi库的出现统一了两者。

fcgi是开发FastCGI程序常用的一个函数库:https://github.com/FastCGI-Archives/fcgi2.git

  • fcgi库把 socket数据收发 和 解析和封装FastCGI数据 封装成函数。方便开发者着眼于业务处理。
  • fcgi库在解析完FastCGI数据后会模拟CGI的规范,设置环境变量和重定向标准输入。(读取数据)
  • 利用fcgi编写的程序也可以当做cgi程序运行。(处理数据)
  • 运行完毕,把数据数据发送给进来时的socket(输出数据)(fastcgi程序把数据从标准输出 放到socket发送给nginx web服务器)
  • nginx web把数据发送给 来时的浏览器

这个fastcgi程序完成了一个返回客户端IP地址的功能。

/***** test_cgi.c* 测试刚刚安装的fastcgi* */
#include <stdio.h>
#include <stdlib.h>     //获取环境变量:环境变量是web收到后被fcgi库设置进去的
#include <fcgi_stdio.h> // fcgi的头文件都在 /usr/local/include/ 目录下// fcgi的库文件都在 /usr/local/lib/ 目录下   -l fcgi
int main()
{while (FCGI_Accept() >= 0) //FCGI_Accept()内开个socket,等着的服务器过来{                          //while循环内,把自己当做是CGI程序来写代码即可//while循环内,发送到标准输出的数据,被socket接收发给Nginx Web服务器printf("Content-Type:text\r\n\r\n");printf("clint ip is %s\r\n", getenv("REMOTE_ADDR"));}return 0;
}
测试:作为CGI程序已经OK了[root@lwh testcpp]# make
[root@lwh testcpp]# REMOTE_ADDR=192.168.134.1 ./execute
Content-Type:textclint ip is 192.168.134.1
[root@lwh testcpp]#
  • 上边的代码中并没有体现守护进程和socket收发数据,所以我们需要借助一个fastcgi程序的管理器帮助。
  • spawn-fcgi是一个通用的选择。
  • spawn-fcgi 帮助我们把fastcgi程序启动为守护进程并设置进去 监听IP和Port
  • 命令spawn-fcgi -a 127.0.0.1 -p 55555-f execute的意思是:按照守护模式启动execute程序,并且监听本地地址(127.0.0.1)的55555端口。

需求: 用户访问http://XXXXXXXXX/ip时,显示用户的IP
步骤:

  1. 使用fcgi库编写FastCGI程序(上边的例子)。编译成可执行文件execute
  2. 执行spawn-fcgi -a 127.0.0.1 -p 55555-f execute启动execute程序。
  3. 在nginx配置文件中增加如下配置后重启nginx(nginx -s reload)
          location /fast/{include fastcgi_params;fastcgi_pass 127.0.0.1:55555;}
  1. 在浏览器上访问nginx所在虚拟机的ip和nginx web监听的端口 192.168.184.134:80/fast/
    以下数据就是经过nginx中的fastcgi处理后 回复回来的

测试:fastcgi程序 作为守护进程和socket收发数据
[root@lwh testcpp]# spawn-fcgi -a 127.0.0.1 -p 55555 -f ./execute
spawn-fcgi: child spawned successfully: PID: 17692[root@lwh testcpp]# vim /usr/local/nginx/conf/nginx.conflocation /fast/{include fastcgi_params;fastcgi_pass 127.0.0.1:55555;}
[root@lwh testcpp]# nginx  -s reload        在浏览器上访问nginx所在虚拟机的ip和nginx web监听的端口 182.168.184.134:80/fast/
浏览器收到数据  clint ip is 192.168.184.1


3.5 fastcgi程序的开发技巧小结

  • 使用fcgi库时的三要素:

    • while (FCGI_Accept() >= 0)循环内写业务

    • getenvfread(buf, sizeof(buf), 1, stdin)获取用户的请求

    • printf向用户展示数据;数据格式是

      • 若干行回复数据头(最简形式Content-Type:text\r\n
      • 一个空行
      • 回复数据体
  • spawn-cgi启动fastcgi程序时要和nginx的fastcgi_pass配置项对应好

  • 良好的设计是:不同目的的请求用不同的FastCGI程序处理。



3.6 演示3:客户端(浏览器)向服务器发送了一个url:

http://www.baidu.com/login?user=zhang3&passwd=123456

  1. Nginx Web服务器, 处理不了动态数据, 但是可以将这些数据转发给其他的进程进行处理

    • 数据转发给程序员编写的fastCGI程序
    • 那么如何配置Nginx的数据转发
http://www.baidu.com/login?user=zhang3&passwd=123456
# 里边有用户提交的数据, nginx不能直接处理, 需要数据转发
# 需要添加对应的处理指令 - 也就是需要添加一个location# location 指令# 指令: 去掉协议、ip/域名、端口号、尾部的文件名, ?和它后边的部分# 剩下的: /loginlocation /login
{# 处理数据转发, fastcgi进程fastcgi_pass IP:Port;include fastcgi.conf; # 包含一个配置文件, 里边是http相关的环境变量   /usr/local/nginx/conf/fastcgi.conf
}# 说明
fastcgi_pass IP:Port# IP: 数据的接收者对应的主机的IP地址,# 如果是本机: localhost、127.0.0.1、192.168.1.101(本机IP)# 如果不是本机: 192.168.1.100# Port: 数据接收者监听的端口, 找一个空闲端口即可
include fastcgi.conf# fastcgi.conf, 和nginx.conf在同一级目录中# /usr/local/nginx/conf#改完配置后要重新加载配置文件
nginx -s reload
  1. 编写fastCGI程序
一般情况下套接字通信时,是利用通信的文件描述符fd,然后read/recv、write/send
fastCGI程序中,直接利用getchar()/fread()和putchar()/write(),对终端操作,fastcgi就可以和nginx通信
// 需要引用的头文件
#include "fcgi_config.h"
#include "fcgi_stdio.h"// 函数: FCGI_Accept() - 阻塞函数
// 当fastcgi进程接收到数据之后, 该函数解除阻塞// 根据环境变量名取出对应的值
char *getenv(const char *name); // 建议:每个fastCGI程序处理单一的功能
while(FCGI_Accept() >= 0)// 进入循环 进行逻辑处理
{// 1. 接收客户端发送的post数据,可以根据content-length长度接收数据size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);// fread(buf, 1, len, stdin);// ptr: 接收数据缓冲区地址// size: 接收的数据块的大小// nmemb: 要接收多少个数据块// stream: 文件指针// 1. 接收客户端发送的get数据// 通过环境变量 QUERY_STRING 将请求行的第二部分数据取出, ?后的一部分// 2. 逻辑处理 - 处理(登陆, 注册, 上传, 下载)// 3. 将处理结果发送给web服务器// -- 除了发送处理的结果, 还必须要组织一个content-type头, 对处理结果的数据格式进行描述
}
  1. 利用spawn-fcgi启动fastcgi程序
# shell命令
spawn-fcgi -a IP -p port -f fastcgi应用程序# IP port: 需要和nginx.conf总配置的fastcgi_pass中的IP Port对应# fastcgi_pass中是localhost 此处写127.0.0.1# fastcgi_pass中是127.0.0.1 此处写127.0.0.1# fastcgi_pass中是192.168.1.101 此处写192.168.1.101


4. 补充知识点

1. 客户端使用Post方式提交数据

  • Http协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必 须使用什么编码方式。
  • 开发者完全可以自己决定消息主体的格式
  • 数据发送出去,还要服务端解析成功才有意义, 服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。

消息主体的常用格式

  • application/x-www-form-urlencoded
# 请求行
POST http://www.example.com HTTP/1.1
# 请求头
Content-Type: application/x-www-form-urlencoded;charset=utf-8
# 空行
# 请求数据(向服务器提交的数据)
title=test&user=kevin&passwd=32222
  • application/json
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
  • text/xml
POST http://www.example.com HTTP/1.1
Content-Type: text/xml
<?xml version="1.0" encoding="utf8"?>
<methodcall><methodname color="red">examples.getStateName</methodname><params><value><i4>41</i4></value></params>
</methodcall>
  • multipart/form-data
POST http://www.example.com HTTP/1.1
Content-Type: multipart/form-data
# 发送的数据
------WebKitFormBoundaryPpL3BfPQ4cHShsBz \r\n
Content-Disposition: form-data; name="file"; filename="qw.png"
Content-Type: image/png\r\n; md5="xxxxxxxxxx"
\r\n
.............文件内容................
.............文件内容................
------WebKitFormBoundaryPpL3BfPQ4cHShsBz--
Content-Disposition: form-data; name="file"; filename="qw.png"
Content-Type: image/png\r\n; md5="xxxxxxxxxx"
\r\n
.............文件内容................
.............文件内容................
------WebKitFormBoundaryPpL3BfPQ4cHShsBz--

2. strtol 函数

// 将数字类型的字符串 -> 整形数
long int strtol(const char *nptr, char **endptr, int base);// 参数nptr: 要转换的字符串 - 数字类型的字符串: "123", "0x12", "0776"// 参数endptr: 测试时候使用, 一般指定为NULL// 参数base: 进制的指定// 10 , nptr = "123456"// 8 , nptr = "0345"// 16, nptr = "0x1ff"
// 转换失败时,从出错位置开始打印
char* p = "123abc";
char* ptErr = NULL;
strtol(p, &ptErr, 10);// 打印ptErr的值: "abc"

https://tool.oschina.net/

5. fastCGI复习

  • 是什么?

    • CGI - 公共网关接口, 一段运行在web服务器上的程序
  • 干什么?
    • 帮助web服务器处理用户提交的动态数据
  • 怎么干?
    • 需要程序员先安装fastCGI
    • 使用fastCGI提供的api进行编码 - 处理业务
while(FCGI_accept() >= 0)
{// 业务处理流程
}
  • fastCGI程序编写完成,如何启动?

    • 使用fastCGI管理器启动 - spawn-fcgi(需要安装)
spawn-fcgi -a IP -p 端口 -f fastCGI程序
# IP和端口意味着fastcgi程序可以接收该IP和Port对应的数据
  • fastCGI处理的数据是由nginx发送的, nginx如何发送数据的?
# 客户端向nginx发送了请求, 请求中有要处理的数据
http://www.baidu.com/login?username=tom&passwd=123
# 在nginx web服务器端需要根据该url将对应的处理指令提取出来# 去掉协议: http/https# 去掉域名/IP地址# 去掉尾部的文件名# 去掉剩余字符串中的?和其后边的部分# 最终得到的指令为: /loginhttp -> server -> 添加location
# 将要处理的数据转发 -> 对应的fastcgi进程
location /login
{fastcgi_pass IP:port; # fastCGI进程绑定的IP和端口include fastcgi.conf;
}

6 实战

1. 配置静态网页register.html

1.新建资源目录
/usr/local/nginx/login_server
2.
vim /usr/local/nginx/conf/nginx.conflocation /{root  register; index register.html;}
3. [root@lwh register]# vim register.html<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>注册界面</title></head><body><form action="reg/" method="post" accept-charset="UTF-8" enctype="text/plane"><input name="username" type="text" placeholder="用户名"><input name="password" type="password" placeholder="密码"><input type="submit" id="submit" value="提交"></body>
  1. 本地主机访问虚拟机,运行正常


2. 配置动态网页(完成注册页面) register

register.html(同6.1)

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>注册界面</title></head><body><form action="reg/" method="post" accept-charset="UTF-8" enctype="text/plane"><input name="username" type="text" placeholder="用户名"><input name="password" type="password" placeholder="密码"><input type="submit" id="submit" value="提交"></body>

user_opt.h

#ifndef __USER_OPT_H__
#define __USER_OPT_H__
/***** 添加用户* 查询用户是否存在
*/#include <stdio.h>//-------------------添加用户-----------
void AddUser(const char *_name, const char *_password);//-------------------查询用户是否存在-----------
//登录:登录时查询到就可以登录
//注册:不存在就可以注册
bool CheckUser(const char *_name, const char *_password_dgst);// :vsplit user_opt.c
#endif

user_opt.cpp

#include "user_opt.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>//-------------------把用户和密码摘要 存到指定文件中-----------
void AddUser(const char *_name, const char *_password)
{char buff[1024] = {0};sprintf(buff, "./add_user.sh %s %s", _name, _password);system(buff);
}
/*
system()函数添加用户,通过调用脚本来实现
什么时候适合system()函数调用脚本: 不关心返回值时,一锤子买卖
*///-------------------查询用户是否存在-----------
//登录:登录时查询到就可以登录
//注册:不存在就可以注册
bool CheckUser(const char *_name, const char *_password_dgst)
{bool bRet = false;if (NULL == _password_dgst) //检测用户是否已经存在{if (fork() > 0){int iStatus = 0; //记录子进程的退出码wait(&iStatus);if (0 == iStatus){bRet = true; //用户已经存在}}else{execlp("./check_user.sh", "./check_user.sh", _name, NULL);}}return bRet;
}
/*
fork()+exec()形式调用脚本:关心脚本是否执行成功
*/
add_user.sh
#!/bin/bash# 对不定长度的数据计算摘要,得出固定长度的签名
#本脚本 负责  把用户名和密码的摘要 存入指定文件USERNAME=$1;
PASSWORD=$2;
PASSWORD=`echo -n ${PASSWORD} | openssl dgst -md5 -hex | awk '{print $NF}'`
echo "${USERNAME}:${PASSWORD}" >> userfile
check_user.sh
#!/bin/bash# 检测user 是否存在
# -w 全字匹配
#  2>&1  > /dec/null 标准错误和标准输出,都重定向到 /dec/null
cat userfile | grep -w $1  2>&1  > /dev/null# [root@lwh game]# ./check_user.sh abc
#[root@lwh game]# echo $?
#0 # 表示上条语句执行成功,即存在user=abc
#[root@lwh game]# ./check_user.sh abcddd
#[root@lwh game]# echo $?
#1 # 表示上条语句没有执行成功,即不存在user=abc
#[root@lwh game]#

register_fcgi.cpp

// 处理注册请求
#include <string>
#include "user_opt.h"
#include <stdlib.h>
#include <fcgi_stdio.h> //fastcgi的头文件,其他放在头文件的最下面
using namespace std;
int main()
{while (FCGI_Accept() >= 0){//获取数据char buff[1024] = {0};fread(buff, 1, sizeof(buff), stdin); //username=hello&password=12345678string orig_string(buff);//取出数据int and_pos = orig_string.find("&");string username = orig_string.substr(9, and_pos - 9);string password = orig_string.substr(and_pos + 10, orig_string.size() - and_pos - 10);//返回数据printf("Content-Type=text\r\n\r\n");if (true == CheckUser(username.c_str(), NULL)){// user existprintf("User Exist!\r\n");}else{AddUser(username.c_str(), password.c_str());// registr successprintf("Register success!\r\n");}}return 0;
}

makefile

#---------------------------------------------------------
# 生成可执行文件 execute
PROJECT     = register_fastcgi
#---------------------------------------------------------
# .o文件
SrcSuf      = c
SrcSuf2     = cpp
SrcSuf3     = cc
ObjSuf      = o
LibSuf      = so
LibSuf2     = a
#---------------------------------------------------------OBJFILES    =  ./user_opt.$(ObjSuf)
OBJFILES    +=  ./register_fcgi.$(ObjSuf)       #---------------------------------------------------------
# 头文件目录
INCLUDEPATH =  -I /usr/include/
INCLUDEPATH += -I /usr/local/include # 库目录
LIBPATH     =  -L /usr/local/lib/
LIBPATH     += -L /usr/lib/
#---------------------------------------------------------
CC          =   g++# 编译选项
CFlag       =   $(INCLUDEPATH) -w -g -ggdb -fshort-wchar -std=c++11 -pthread# 链接选项
LDFLAGS     +=  $(LIBPATH)
LDFLAGS     +=  -l pthread
#LDFLAGS     +=  -l hiredis
LDFLAGS     +=  -l fcgi
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf) .$(SrcSuf2) .$(LibSuf2) .$(SrcSuf3)all:  $(PROJECT) clean
# 生成可执行文件
$(PROJECT):$(OBJFILES)@echo "creating $(PROJECT) start..."$(CC) $(LDFLAGS) $(OBJFILES) -o $(PROJECT) @echo "creating $(PROJECT) end"
#---------------------------------------------------------
# .c 生成 .o 文件
.$(SrcSuf).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CC) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# .cpp 生成 .o 文件
.$(SrcSuf2).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# .cc 生成 .o 文件
.$(SrcSuf3).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# 删除 .o 文件
clean:@echo "Cleaning $(PROJECT) project files"@rm -f $(OBJFILES) core#---------------------------------------------------------
# make cleanall 删除 .o 文件 execute文件
.PHONY:cleanobj
cleanobj:-rm -f $(OBJFILES)
.PHONY:cleanexe
cleanexe:-rm -f $(PROJECT)
.PHONY:cleanall
cleanall:cleanobj cleanexe

运行

脱离nginx运行本fastcgi程序[root@lwh game]# echo 'username=hello&password=123456789' | ./register_fastcgi
Content-Type=textRegister success!
[root@lwh game]# echo 'username=hello&password=123456789' | ./register_fastcgi
Content-Type=textUser Exist!
[root@lwh game]# 运行正常
结合nginx 运行本fastcgi程序 启动fastcgi程序
[root@lwh game]# spawn-fcgi -a 127.0.0.1 -p 55555 -f ./register_fastcgi
spawn-fcgi: child spawned successfully: PID: 57820
[root@lwh game]# ps aux | grep register_fastcgi
root      57820  0.0  0.0  19076   900 ?        Ss   22:11   0:00 ./register_fastcgi
root      57841  0.0  0.0 110280   892 pts/0    S+   22:11   0:00 grep --color=auto register_fastcgi改nginx的配置文件
[root@lwh game]# nginx
[root@lwh conf]# vim nginx.conflocation /{root  register; index register.html;}location /reg/ {include         fastcgi.conf;fastcgi_pass    127.0.0.1:55555;}
重新加载配置文件
[root@lwh conf]# nginx -s reload运行运行正常:见下图

访问192.168.184.134 (虚拟机地址)

输入 用户名 和 密码 点击 提交

再次提交数据

整体流程图!!!!!!

6.3 配置动态网页(完成登录页面) login

register.html(同6.1)

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>注册界面</title></head><body><form action="reg/" method="post" accept-charset="UTF-8" enctype="text/plane"><input name="username" type="text" placeholder="用户名"><input name="password" type="password" placeholder="密码"><input type="submit" id="submit" value="提交"></body>

user_opt.h

#ifndef __USER_OPT_H__
#define __USER_OPT_H__
/***** 查询用户是否存在
*/#include <stdio.h>//-------------------查询用户是否存在-----------
//登录:登录时查询到就可以登录
bool CheckUser(const char *_name, const char *_password_dgst);// :vsplit user_opt.c
#endif

user_opt.cpp

#include "user_opt.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>//-------------------查询用户是否存在-----------
//登录:登录时查询到就可以登录
bool CheckUser(const char *_name, const char *_password_dgst)
{bool bRet = false;if (fork() > 0){int iStatus = 0; //记录子进程的退出码wait(&iStatus);if (0 == iStatus){bRet = true; //用户已经存在}}else{execlp("./check_user.sh", "./check_user.sh", _name, _password_dgst, NULL);}return bRet;
}
/*
fork()+exec()形式调用脚本:关心脚本是否执行成功
*/
check_user.sh
#!/bin/bash# 检测 user 是否存在: 用户名和密码摘要要完全匹配!!!
# -w 全字匹配
#  2>&1  > /dec/null 标准错误和标准输出,都重定向到 /dec/null
if [ "$2" == "" ]
thencat ../game_reg_cgi/userfile | grep -w "$1"  2>&1  > /dev/null
elsecat ../game_reg_cgi/userfile | grep -w "$1:$2"  2>&1  > /dev/null
fi# [root@lwh game]# ./check_user.sh abc
# [root@lwh game]# echo $?
# 0
# 表示上条语句执行成功,即存在user=abc# [root@lwh game_login_cgi]# ./check_user.sh abc c4ca4238a0b923820dcc509a6f75849b
# [root@lwh game_login_cgi]# echo $?
# 0
# 用户存在且密码正确# [root@lwh game_login_cgi]# ./check_user.sh abc c4ca4238a0b923820dcc509a6f75849
# [root@lwh game_login_cgi]# echo $?
# 1 #密码不对# [root@lwh game]# ./check_user.sh abcddd
# [root@lwh game]# echo $?
# 1
# 表示上条语句没有执行成功,即不存在user=abc

login_fastcgi.cpp

// 处理登录请求
#include "user_opt.h"
#include <string>
#include <stdlib.h>
#include "CJsonObject.hpp"
using namespace std;#include <fcgi_stdio.h>
int main()
{while (FCGI_Accept() >= 0){string username;string password;//1.1 读取数据unsigned int json_length = atoi(getenv("CONTENT_LENGTH"));char *buf = (char *)calloc(1UL, json_length);fread(buf, 1, json_length, stdin);string json_string(buf);//1.2 取出json串中对应的值neb::CJsonObject json(json_string);json.Get("username", username);json.Get("password", password);//2.回复一个json串//构建回复的HTTP身体neb::CJsonObject reply;if (true == CheckUser(username.c_str(), password.c_str())){//reply ok to clientreply.Add("login_result", "OK");}else{//reply failed to clientreply.Add("login_result", "Failed");}string reply_string = reply.ToFormattedString(); //转换为json格式的string//2.1 回复的HTTP头printf("Content-Type:application/json\r\n");printf("Content-Length:%d\r\n\r\n", reply_string.size());//2.2 回复的HTTP身体printf("%s\r\n", reply_string.c_str());free(buf);}
}

makefile

#---------------------------------------------------------
# 生成可执行文件 execute
PROJECT     = login_fastcgi
#---------------------------------------------------------
# .o文件
SrcSuf      = c
SrcSuf2     = cpp
SrcSuf3     = cc
ObjSuf      = o
LibSuf      = so
LibSuf2     = a
#---------------------------------------------------------OBJFILES    +=  ./cJSON.$(ObjSuf)
OBJFILES    +=  ./CJsonObject.$(ObjSuf)
OBJFILES    +=  ./login_fastcgi.$(ObjSuf)
OBJFILES    +=  ./user_opt.$(ObjSuf)   #---------------------------------------------------------
# 头文件目录
INCLUDEPATH =  -I /usr/include/
INCLUDEPATH += -I /usr/local/include # 库目录
LIBPATH     =  -L /usr/local/lib/
LIBPATH     += -L /usr/lib/
#---------------------------------------------------------
CC          =   g++# 编译选项
CFlag       =   $(INCLUDEPATH) -w -g -ggdb -fshort-wchar -std=c++11 -pthread# 链接选项
LDFLAGS     +=  $(LIBPATH)
LDFLAGS     +=  -l pthread
#LDFLAGS     +=  -l hiredis
LDFLAGS     +=  -l fcgi
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf) .$(SrcSuf2) .$(LibSuf2) .$(SrcSuf3)all:  $(PROJECT) clean
# 生成可执行文件
$(PROJECT):$(OBJFILES)@echo "creating $(PROJECT) start..."$(CC) $(LDFLAGS) $(OBJFILES) -o $(PROJECT) @echo "creating $(PROJECT) end"
#---------------------------------------------------------
# .c 生成 .o 文件
.$(SrcSuf).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CC) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# .cpp 生成 .o 文件
.$(SrcSuf2).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# .cc 生成 .o 文件
.$(SrcSuf3).$(ObjSuf):@echo "Compiling $(PROJECT) $<"$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# 删除 .o 文件
clean:@echo "Cleaning $(PROJECT) project files"@rm -f $(OBJFILES) core#---------------------------------------------------------
# make cleanall 删除 .o 文件 execute文件
.PHONY:cleanobj
cleanobj:-rm -f $(OBJFILES)
.PHONY:cleanexe
cleanexe:-rm -f $(PROJECT)
.PHONY:cleanall
cleanall:cleanobj cleanexe

运行

脱离nginx,在本机直接测试login_fastcgi程序[root@lwh game_login_cgi]# echo '{"password": "c4ca4238a0b923820dcc509a6f75849b", "username": "abc"}' | CONTENT_LENGTH=67 ./login_fastcgi
Content-Type:application/json
Content-Length:25
{"login_result": "OK"
}
[root@lwh game_login_cgi]# 运行正常
结合nginx 运行register_fastcgi、login_fastcgi程序 启动fastcgi程序
[root@lwh game_login_cgi]# spawn-fcgi -a 127.0.0.1 -p 55556 -f ./login_fastcgi
spawn-fcgi: child spawned successfully: PID: 112910[root@lwh game_login_cgi]# nginx
[root@lwh game_login_cgi]# [root@lwh game_reg_cgi]# spawn-fcgi -a 127.0.0.1 -p 55555 -f ./register_fastcgi
spawn-fcgi: bind failed: Address already in use
[root@lwh game_reg_cgi]# ps aux|grep register_fastcgi
root      62310  0.0  0.0  19080   760 ?        Ss   02:38   0:00 ./register_fastcgi
root     115339  0.0  0.0 110284   900 pts/2    S+   16:44   0:00 grep --color=auto register_fastcgi
[root@lwh game_reg_cgi]# 改nginx的配置文件
[root@lwh game]# nginx
[root@lwh conf]# vim nginx.conflocation /{root  register; index register.html;}location /reg/ {include         fastcgi.conf;fastcgi_pass    127.0.0.1:55555;}location /login/{   include         fastcgi.conf;fastcgi_pass    127.0.0.1:55556;}
重新加载配置文件
[root@lwh conf]# nginx -s reload运行运行正常:见下图


测试:Qt窗口和nginx服务器之间json串传是否正常

192.168.184.134 (虚拟机地址)启动nginx和login_fastcgi程序
在Qt搭建的登录窗口中,输入 用户名 和 密码 点击 提交
nginx和login_fastcgi程序处理完毕回复给 Qt程序
Qt程序打印收到的json字符串 (login_fastci程序封装的HTTP数据身体)



测试:用户名或者密码错误时,弹出失败提示窗口



测试 点击注册按钮
Qt程序

  1. 点击注册 (跳到浏览器,并访问nginx的register.html网页)
  2. 跳到浏览器 ,输入待注册的用户名和密码,提交(测试注册)
    (此时nginx的register_fastcgi负责处理)
  3. 测试登录
    (此时nginx的login_fastcgi负责处理)


CGI,FastCGI,spawn-fcgi,nginx组合使用相关推荐

  1. Nginx + CGI/FastCGI + C/Cpp

    http://www.cnblogs.com/skynet/p/4173450.html Nginx + CGI/FastCGI + C/Cpp 2014-12-19 11:05 by 吴秦, 197 ...

  2. Nginx + CGI/FastCGI + C/Cpp(编不过去,不搞了。。。)(Common Gateway Interface)

    Nginx + CGI/FastCGI + C/Cpp 文章目录 1.CGI 1.1.环境变量 1.2.标准输入 总结:CGI使外部程序与Web服务器之间交互成为可能.CGI程式运行在独立的进程中,并 ...

  3. 漫谈CGI FastCGI WSGI

    作者:auxten 链接:https://zhuanlan.zhihu.com/p/20054757 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. CGI(Comm ...

  4. cgi,fastcgi,php-cgi,php-fpm之间的关系

    参考文章: https://segmentfault.com/q/1010000000256516 http://www.thinkphp.cn/topic/42338.html 这篇文章写的挺好:h ...

  5. php cgi fastcgi php-fpm区别

    CGI 的作用 CGI 是为了保证 web server 传递过来的数据是标准格式的,方便 CGI 程序的编写者. web server(比如说 nginx)只是内容的分发者. 如果请求 /index ...

  6. fastcgi pass php-fpm,nginx: fastcgi_pass的配置

    在做nginx配置过程中,对于在与php-fpm选择哪种通信模式有些疑问,故补充相关知识点并记录于此. 说明fastcgi_pass的配置问题前,先理解几个概念: CGI ( Common Gatew ...

  7. nginx fastcgi python_linux下nginx+python+fastcgi部署总结(django版)

    最近因为项目上的需要开始大量使用nginx,因此也想趁机将以前常用的django+apache的架构换成django+nginx+fastcgi,此文是整个搭建的步骤,主要留作备忘,也希望对大家有所帮 ...

  8. windos php7 fast cgi,FAST-CGI解析漏洞拿下网站并提权windows系统服务器

    作为一个懒散的人,我比较喜欢关注网上发布的漏洞.利用最新的漏洞可以比较简单的得到webshell,然后利用webshell提权拿下服务器,这个是我最喜欢的.有一个很有意思的娱乐网站,我经常登陆浏览,以 ...

  9. fastcgi pass php-fpm,Nginx中fastcgi_pass的配置问题

    Nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket. 其中TCP是IP加端口,可以跨服务器.而UNIX Domain Socket不经过网络,只 ...

最新文章

  1. NOIP普及组第1题(1995-2018)
  2. boost::test模块测试可变参数样本元素支持和数据集定义的移动语义
  3. autosys虚拟机定义
  4. 使用ffmpeg 的 filter 给图片添加水印
  5. laravel 5.1 添加第三方扩展库
  6. Android图片特效处理之图片叠加
  7. 光栅图形学——直线段的扫描转换算法
  8. 2018年个人的一些简单预测
  9. 一个微信小程序的案例
  10. 雅思英语作文计算机和历史,关于computer的雅思写作范文
  11. 4.vue常见指令v-for的基本使用
  12. 【年终盘点】2019上海人工智能发展十大事记
  13. 财务管理都学什么计算机课程,财务管理都学什么课程
  14. 第九章 java常用类
  15. windows局域网传输文件
  16. 快手校园招聘工程笔试B卷-搭积木
  17. Html颜色颜色选择器小技巧
  18. 产能利用率 (Capacity Utilization)
  19. 网约车司机被取代?百度获得中国首个全无人驾驶出租车服务许可证
  20. RPC在Python中的使用及原理浅析

热门文章

  1. 测试工程师数据库面试题
  2. 在deepin上如何打开PowerDesigner文件或者pdm文件
  3. MATLAB激活成功后打开还是激活界面的解决方案
  4. Windows下安装tdm-gcc(64位)
  5. S7-200 SMART PLC 子程序功能块(阀门控制)
  6. matdem矩阵维度必须一致_离散元软件 MatDEM 学习笔记:前言
  7. 石油币,世界第一个主权加密数字货币.md
  8. 高德地图:实现3D模型轨迹回放效果(类似百度地图汽车模型路书)
  9. Node中实现一个简易的图片验证码流程
  10. 期货中的“心静”和“身静”