用C语言开发FastCGI应用程序——fcgi_stdio包API

本文详细介绍了FastCGI软件开发套件,旨在简化现有CGI程序转换为FastCGI程序或编写新FastCGI程序的过程。重点介绍了fcgi_stdio和fcgiapp两个函数包的使用,以及如何通过它们实现二进制文件在CGI和FastCGI之间的完美兼容。文章还提供了实例代码,展示了如何使用FastCGI进行高效响应处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

官方文档:http://www.fastcgi.com/devkit/doc/fcgi-devel-kit.htm

官网头文件:http://www.fastcgi.com/devkit/include/fcgi_stdio.h

简介

FastCGI软件开发套件用于简化把现有的CGI程序转成FastCGI程序或者编写FastCGI程序。

  I/O函数包

  在开发套件中有两个函数包:fcgi_stdio 和 fcgiapp,在你的程序中必须包含这些包中的一个:

  fcgi_stdio.h fcgiapp.h

        fcgi_stdio包,是fcgiapp包的顶层包,在转化CGI程序或者是写新的FastCGI程序是,我们强烈推荐你用它。用这个库,你的二进制程序既可以当作CGI也可以当作FASTCGI程序来运行(原文:Thefcgi_stdio library provides the easiest transition for C CGI programs and C CGI programmers to FastCGI. Using this library your application can run using either CGI or FastCGI, with the same binary for both situations.)。

        fcgiapp包是另一个fastcgi库。它没有像fcgi_stdio包一样提供那么多兼容性;所以,它也没有使用那么多宏。fcgi_stdio包是运行在fcgiapp上的一层。

                基于fcgiapp包运行的程序不能当作CGI程序来运行。这种特性只在fcgi_stdio层提供。

                fcgiapp包中的函数以 FCGX_ 开头,而不是以 FCGI_ 开头。比如:FCGX_Accept FCGI_Accept 的fcgiapp版本。

                fcgiapp包中的函数的描述文档以扩展的注释方式包括在头文件include/fcgiapp.h 中。范例程序examples/tiny-fcgi2.cexamples/echo2.c 说明如果使用fcgiapp包。

        (原文:

The fcgiapp library is a second C library for FastCGI. It does not provide the high degree of source code compatibility provided byfcgi_stdio; in return, it does not make such heavy use of#define.fcgi_stdio is implemented as a thin layer on top of fcgiapp.

Applications built using the fcgiapp library cannot run as CGI programs; that feature is provided at thefcgi_stdio level.

Functions defined in fcgiapp are named using the prefix FCGX_ rather thanFCGI_. For instance,FCGX_Accept is thefcgiapp version ofFCGI_Accept.

Documentation of the fcgiapp library takes the form of extensive comments in the header fileinclude/fcgiapp.h. The sample programsexamples/tiny-fcgi2.c andexamples/echo2.c illustrate how to usefcgiapp.)

        本文主要介绍fcgi_stdio包,它有以下几个优点:

  简单:只要有三个新的API需要学。

  易懂:如果你正把CGI程序转化为FastCGI程序,你会发现CGI程序与FastCGI程序之间只有很少的区别。在我们设计函数库的时候我们尽可能的 把FastCGI应用程序变得容易理解,以至于我们在建立新FastCGI程序的时候我们使用相同的环境变量,相同的解析查询字符串的技术,以及相同的 I/O程序等。

  方便:这个库函数提供了CGI和FastCGI二进制的文件的完美兼容。因此不管是CGI还是FastCGI,都同样运行。

  fcgi_stdio包开发的fastcgi代码结构

  FastCGI的代码构成,把你的代码分成两个独立部分:

  1.初始化部分:只执行一次

  2.应答循环部分:FastCGI脚本每被调用一次,这部分就被执行一次

  

  一个应答循环的典型格式如下:

  while (FCGI_Accept() >= 0) {//循环条件

  # 应答循环体

  }

  

  直到一个客户端请求来的时候FCGI_Accept块才执行,并返回0。如果有一个系统故障,或是系统管理员终止进程,Accept将返回-1。

  如果应用程序作为一个CGI程序被调用,那么第一次调用Accept时,返回0,第二次总是返回-1,产生CGI行为。(请详见20页的"FCGI_Accept (3)" )

  注意,在CGI中鼓励用小脚本,然而在FastCGI中则鼓励使用组合式的脚本。你可以在重新构想你的程序的全局结构,来获得FastCGI的高性能。

  例1: TinyFastCGI

  这是一个用C语言写的一个简单FastCGI应答程序例子:

#include "fcgi_stdio.h"
#include <stdlib.h>

int count;

void initialize(void)
{
    count=0;
}
void main(void)
{
    initialize();
    while (FCGI_Accept() >= 0)
    {
        printf( "Content-type: text/html\r\n"
            "\r\n"
            "<title>FastCGI Hello! (C, fcgi_stdio library)</title>"
            "<h1>FastCGI Hello! (C, fcgi_stdio library)</h1>"
            "Request number %d running on host <i>%s</i>\n",
            ++ count, getenv( "SERVER_HOSTNAME" ) );
  }
}

  例2:原始数据产生器

  思考一下,一个应答应用程序产生第N次原始数据。

  一个CGI应用程序将没有有效的方法来解决这个问题。例如,如果用户访问第50000次的原始数据,那么CGI应用程序就不许从第一条原始数据开始计算,直到第50000条的,要是应用程序终止,伴随着她辛苦积累的数据也会随之消失。

  如果一个客户端想访问第4900条原始数据,那么服务器必须重新开始积累。

  由于我们能维持这个状态,FastCGI应用程序对与这样的问题就更有效。一个FastCGI应用程序在初始化阶段能够计算一个扩展的源数据的表,并保持表的不同范围。当客户端请求一个特别原始数据的时候,循环应答需要从表里查询。

  这里有一个原始数据代码事例:

  

#include "fcgi_stdio.h"
#include <stdlib.h>
#include <string.h>

#define POTENTIALLY_PRIME 0
#define COMPOSITE 1
#define VALS_IN_SIEVE_TABLE 1000000
#define MAX_NUMBER_OF_PRIME_NUMBERS 78600

long int sieve_table[VALS_IN_SIEVE_TABLE];
long int prime_table[MAX_NUMBER_OF_PRIME_NUMBERS];

void initialize_prime_table(void)
{
    long int prime_counter = 1;
    long int current_prime = 2, c, d;
    
    prime_table[prime_counter] = current_prime;
    while ( current_prime < VALS_IN_SIEVE_TABLE )
    {
        for ( c = current_prime; c <= VALS_IN_SIEVE_TABLE; c += current_prime )
        {
            sieve_table[c] = COMPOSITE;
        }
        for ( d=current_prime+1; sieve_table[d] == COMPOSITE; d++ );
            prime_table[++prime_counter] = d;
        current_prime = d;
    }
}

void main(void)
{
    char *query_string;
    long int n;
    initialize_prime_table();
    
    while( FCGI_Accept() >= 0 )
    {
        printf( "Content-type: text/html\r\n\r\n" );
        printf( "<title>Prime FastCGI</title>\n"
            "<h1>Prime FastCGI</h1>\n" );
        query_string = getenv( "QUERY_STRING" );
        if( query_string == NULL )
        {
            printf("Usage: Specify a positive number in the query string.\n");
        }
        else
        {
            query_string = strchr(query_string, '=') + 1;
            n = strtol(query_string);
            if ( n < 1 )
            {
                printf( "The query string '%s' is not a positive number.\n", query_string );
            }
            else if ( n > MAX_NUMBER_OF_PRIME_NUMBERS )
            {
                printf( "The number %d is too large for this program.\n", n );
            }
            else
            {
                printf( "The %ldth prime number is %ld.\n", n, prime_table[n] );
            }
        }
    }
}

  这个应用程序在初始化时有一个显而意见的开销,但是后来的访问是快速的。

### FastCGI协议详解及实现方式 #### 什么是FastCGIFastCGI是一种用于Web服务器和应用程序之间通信的协议。它改进了传统的CGI(Common Gateway Interface),解决了后者每次请求都需要启动新进程带来的性能瓶颈问题[^3]。 #### FastCGI的工作原理 与传统CGI相比,FastCGI使用持久化进程来处理一系列请求,而非为每个请求创建新的进程。当接收到HTTP请求时,Web服务器会将环境变量以及页面请求通过Socket或TCP连接发送到FastCGI进程。这些进程由专门的FastCGI管理器维护,而不是由Web服务器直接控制[^3]。 #### FastCGI的优点 - **高效性**:由于采用常驻进程的方式,减少了频繁创建和销毁进程所带来的开销。 - **可扩展性**:支持跨网络调用,允许FastCGI应用部署在不同的物理机器上。 - **资源共享能力增强**:能够更好地利用数据库连接池、内存缓存等资源[^4]。 #### C语言中的简单FastCGI实现示例 以下是基于C语言的一个简化版FastCGI客户端实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define FCGI_VERSION_1 1 #define FCGI_BEGIN_REQUEST 1 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_END_REQUEST 3 typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header; void send_fcgi_record(int sock, int type, int request_id, const void *content, size_t length) { FCGI_Header header; memset(&header, 0, sizeof(header)); header.version = FCGI_VERSION_1; header.type = type; header.requestIdB1 = (request_id >> 8) & 0xFF; header.requestIdB0 = request_id & 0xFF; header.contentLengthB1 = (length >> 8) & 0xFF; header.contentLengthB0 = length & 0xFF; write(sock, &header, sizeof(FCGI_Header)); if (content && length > 0) { write(sock, content, length); } } int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字 struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9000); // 假设FastCGI服务监听于9000端口 inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr)); connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); // 发送BEGIN_REQUEST记录 typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; FCGI_BeginRequestBody beginBody; memset(&beginBody, 0, sizeof(beginBody)); beginBody.roleB1 = 0; // Responder角色 beginBody.flags = 0; send_fcgi_record(sockfd, FCGI_BEGIN_REQUEST, 1, &beginBody, sizeof(beginBody)); // 发送PARAMS记录 const char* params[] = {"SCRIPT_FILENAME=/path/to/script.php", NULL}; for (size_t i = 0; params[i]; ++i) { send_fcgi_record(sockfd, FCGI_PARAMS, 1, params[i], strlen(params[i])); } // 结束PARAMS传输 send_fcgi_record(sockfd, FCGI_PARAMS, 1, NULL, 0); close(sockfd); } ``` 此代码片段展示了如何构建并发送基本的FastCGI消息头及其相关内容至指定的服务端[^1]。 #### 配置Nginx以配合FastCGI工作 为了使Nginx能正确地与FastCGI交互,通常需要设置`fastcgi_param`指令定义各种参数。例如,默认配置如下所示[^5]: ``` fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; ... ``` 以上仅列举部分必要项;实际环境中可能还需要调整更多细节适应具体需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值