传统 CGI 工作原理分析
客户端访问某个 URL 地址之后,通过 GET/POST/PUT 等方式提交数据,并通过 HTTP 协议向 Web 服务器发出请求,服务器端的 HTTP Daemon(守护进程)将 HTTP 请求里描述的信息通过标准输入 stdin 和环境变量(environment variable)传递给主页指定的 CGI 程序,并启动此应用程序进行处理(包括对数据库的处理),处理结果通过标准输出 stdout 返回给 HTTP Daemon 守护进程,再由 HTTP Daemon 进程通过 HTTP 协议返回给客户端。
上面的这段话理解可能还是比较抽象,下面我们就通过一次GET请求为例进行详细说明。
下面用代码来实现图中表述的功能。Web 服务器启动一个 socket 监听服务,然后在本地执行 CGI 程序。后面有比较详细的代码解读。
10年架构师领你架构-成长之路-(附面试题(含答案))
(腾讯T3-T4)打造互联网PHP架构师教程目录大全,只要你看完,薪资立马提升2倍(持续更新)
Web 服务器代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#define SERV_PORT 9003
char* str_join(char *str1, char *str2);
char* html_response(char *res, char *buf);
int main(void)
{
int lfd, cfd;
struct sockaddr_in serv_addr,clin_addr;
socklen_t clin_len;
char buf[1024],web_result[1024];
int len;
FILE *cin;
if((lfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("create socket failed");
exit(1);
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
if(bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
perror("bind error");
exit(1);
}
if(listen(lfd, 128) == -1)
{
perror("listen error");
exit(1);
}
signal(SIGCLD,SIG_IGN);
while(1)
{
clin_len = sizeof(clin_addr);
if ((cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len)) == -1)
{
perror("接收错误\n");
continue;
}
cin = fdopen(cfd, "r");
setbuf(cin, (char *)0);
fgets(buf,1024,cin); //读取第一行
printf("\n%s", buf);
//============================ cgi 环境变量设置演示 ============================
// 例如 "GET /user.cgi?id=1 HTTP/1.1";
char *delim = " ";
char *p;
char *method, *filename, *query_string;
char *query_string_pre = "QUERY_STRING=";
method = strtok(buf,delim); // GET
p = strtok(NULL,delim); // /user.cgi?id=1
filename = strtok(p,"?"); // /user.cgi
if (strcmp(filename,"/favicon.ico") == 0)
{
continue;
}
query_string = strtok(NULL,"?"); // id=1
putenv(str_join(query_string_pre,query_string));
//============================ cgi 环境变量设置演示 ============================
int pid = fork();
if (pid > 0)
{
close(cfd);
}
else if (pid == 0)
{
close(lfd);
FILE *stream = popen(str_join(".",filename),"r");
fread(buf,sizeof(char),sizeof(buf),stream);
html_response(web_result,buf);
write(cfd,web_result,sizeof(web_result));
pclose(stream);
close(cfd);
exit(0);
}
else
{
perror("fork error");
exit(1);
}
}
close(lfd);
return 0;
}
char* str_join(char *str1, char *str2)
{
char *result = malloc(strlen(str1)+strlen(str2)+1);
if (result ==