CGI,FastCGI,spawn-fcgi,nginx组合使用
目录
- 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处理流程
- web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输入传递数据
- CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
- CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
- 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
- 客户端给服务器发送http请求, 要求登陆
- 服务器接收客户端请求, 服务器无法处理登陆操作
- 服务器要处理请求, 先创建一个子进程, 目的的帮助服务器解析服务器无法解析的数据
- Cgi程序是由程序员写好之后提供给web服务器的
- cgi程序先进行初始化
- 连接数据库
- web服务器将要处理的数据发送给cgi程序
- cgi程序进行数据库查询
- 将查询结果发送给web服务器
- 服务器将CGI进程销毁
- web服务器将结果发送给客户端
- 使用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处理流程
- Web 服务器启动时载入初始化FastCGI执行环境。 例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
- FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。
- 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发给FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
- FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。
- FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。
fastcgi执行流程
客户端向服务器端发送登录请求
服务器收到登录请求无法处理, 服务器端启动fastCGI进程管理器
在fastCGI进管理器中管理了一个CGI程序
- 进入循环的意思?
- 循环处理服务器发送的请求
- 如果服务器没有发送请求消息, 循环阻塞
fastCGI处理完毕之后, 将结果发送给web服务器
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进程
- 需要指定监听的端口和IP地址, 并对其绑定
- 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
步骤:
- 使用fcgi库编写FastCGI程序(上边的例子)。编译成可执行文件execute
- 执行
spawn-fcgi -a 127.0.0.1 -p 55555-f execute
启动execute程序。 - 在nginx配置文件中增加如下配置后重启nginx(nginx -s reload)
location /fast/{include fastcgi_params;fastcgi_pass 127.0.0.1:55555;}
- 在浏览器上访问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)
循环内写业务用
getenv
和fread(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
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
- 编写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头, 对处理结果的数据格式进行描述
}
- 利用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>
- 本地主机访问虚拟机,运行正常
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程序
- 点击注册 (跳到浏览器,并访问nginx的register.html网页)
- 跳到浏览器 ,输入待注册的用户名和密码,提交(测试注册)
(此时nginx的register_fastcgi负责处理)
- 测试登录
(此时nginx的login_fastcgi负责处理)
CGI,FastCGI,spawn-fcgi,nginx组合使用相关推荐
- 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 ...
- Nginx + CGI/FastCGI + C/Cpp(编不过去,不搞了。。。)(Common Gateway Interface)
Nginx + CGI/FastCGI + C/Cpp 文章目录 1.CGI 1.1.环境变量 1.2.标准输入 总结:CGI使外部程序与Web服务器之间交互成为可能.CGI程式运行在独立的进程中,并 ...
- 漫谈CGI FastCGI WSGI
作者:auxten 链接:https://zhuanlan.zhihu.com/p/20054757 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. CGI(Comm ...
- cgi,fastcgi,php-cgi,php-fpm之间的关系
参考文章: https://segmentfault.com/q/1010000000256516 http://www.thinkphp.cn/topic/42338.html 这篇文章写的挺好:h ...
- php cgi fastcgi php-fpm区别
CGI 的作用 CGI 是为了保证 web server 传递过来的数据是标准格式的,方便 CGI 程序的编写者. web server(比如说 nginx)只是内容的分发者. 如果请求 /index ...
- fastcgi pass php-fpm,nginx: fastcgi_pass的配置
在做nginx配置过程中,对于在与php-fpm选择哪种通信模式有些疑问,故补充相关知识点并记录于此. 说明fastcgi_pass的配置问题前,先理解几个概念: CGI ( Common Gatew ...
- nginx fastcgi python_linux下nginx+python+fastcgi部署总结(django版)
最近因为项目上的需要开始大量使用nginx,因此也想趁机将以前常用的django+apache的架构换成django+nginx+fastcgi,此文是整个搭建的步骤,主要留作备忘,也希望对大家有所帮 ...
- windos php7 fast cgi,FAST-CGI解析漏洞拿下网站并提权windows系统服务器
作为一个懒散的人,我比较喜欢关注网上发布的漏洞.利用最新的漏洞可以比较简单的得到webshell,然后利用webshell提权拿下服务器,这个是我最喜欢的.有一个很有意思的娱乐网站,我经常登陆浏览,以 ...
- fastcgi pass php-fpm,Nginx中fastcgi_pass的配置问题
Nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket. 其中TCP是IP加端口,可以跨服务器.而UNIX Domain Socket不经过网络,只 ...
最新文章
- NOIP普及组第1题(1995-2018)
- boost::test模块测试可变参数样本元素支持和数据集定义的移动语义
- autosys虚拟机定义
- 使用ffmpeg 的 filter 给图片添加水印
- laravel 5.1 添加第三方扩展库
- Android图片特效处理之图片叠加
- 光栅图形学——直线段的扫描转换算法
- 2018年个人的一些简单预测
- 一个微信小程序的案例
- 雅思英语作文计算机和历史,关于computer的雅思写作范文
- 4.vue常见指令v-for的基本使用
- 【年终盘点】2019上海人工智能发展十大事记
- 财务管理都学什么计算机课程,财务管理都学什么课程
- 第九章 java常用类
- windows局域网传输文件
- 快手校园招聘工程笔试B卷-搭积木
- Html颜色颜色选择器小技巧
- 产能利用率 (Capacity Utilization)
- 网约车司机被取代?百度获得中国首个全无人驾驶出租车服务许可证
- RPC在Python中的使用及原理浅析
热门文章
- 测试工程师数据库面试题
- 在deepin上如何打开PowerDesigner文件或者pdm文件
- MATLAB激活成功后打开还是激活界面的解决方案
- Windows下安装tdm-gcc(64位)
- S7-200 SMART PLC 子程序功能块(阀门控制)
- matdem矩阵维度必须一致_离散元软件 MatDEM 学习笔记:前言
- 石油币,世界第一个主权加密数字货币.md
- 高德地图:实现3D模型轨迹回放效果(类似百度地图汽车模型路书)
- Node中实现一个简易的图片验证码流程
- 期货中的“心静”和“身静”