Linux TCP server系列(8)-cgi server
2011-09-18 21:36 Aga.J 阅读(1272) 评论(0) 编辑 收藏 举报
目标:
单进程cgi服务器
CGI介绍:
CGI使在网络服务器下运行外部分应用程序(或网关)成为可能。CGI-BIN
目录是存放CGI脚本的地方。这些脚本使WWW服务器和浏览器能运行外部程序,而无需启动另一个原因程序。 CGI是在HTTP服务器下运行外部程序(或网关)的一个接口,它能让网络用户访问远程系统上的使用类型程序,就好像他们在实际使用那些远程计算机一样。
尽管CGI易于使用,但是当大批人同时使用一个CGI应用程序是会反应较慢,网络服务器 速度也会受到很大 影响。CGI应用程序的优点是可以独立运行。
CGI应用程序可以由大多数的编程语言编写,如Perl(Practical Extraction and Report Language)、C\C++、Java 和Visual Basic等。不过对于那些没有太多编程经验的网页制作人来说,实在是一个不小的难题。
1.浏览器通过HTML表单或超链接请求指上一个CGI应用程序的URL。
2.服务器收发到请求。
3.服务器执行指定所CGI应用程序。
4.CGI应用程序执行所需要的操作,通常是基于浏览者输入的内容。
5.CGI应用程序把结果格式化为网络服务器和浏览器能够理解的文档(通常是HTML网页)。
6.网络服务器把结果返回到浏览器中。
CGI应用程序运行在浏览器可以请求的服务器系统上,执行时需要使用服务器CPU时间和内存。如果有成千上万的这种程序会同时运行,那会对服务器系统提出极高的要求。你要慎重考虑这个问题,以防止服务器系统崩溃。
实现
将简单的单进程server做一点改动即可作为一个CGI服务器。
客户连接并请求到来时(HTTP连接并请求,如 http://localhost:84/cgi-bin/hello.cgi)。服务器解析HTTP头部信息,提取出客户请求的资源位置,然后执行cgi脚本,并将结果加上HTTP头部信息一起返回给客户端。
CGI服务器的CGI处理部分主要由以下部分组成
1 get request resource string
从HTTP请求头中抽取出关键字段
2 find resource
根据字段信息在本地磁盘上查找到目标cgi文件
3 run cgi task
运行cgi脚本(本程序中使用popen函数来执行服务端脚本文件)
4 response cgi result in form of HTTP
构造HTTP响应头,拼接上脚本输出,返回给客户端。
具体实现如下:
void
cgi_echo(int sockfd)
{
ssize_t n;
char request[512];
char tempChar;
int
charIndex=0;
char token[128];
int charIndexInToken=0;
char res[128];
bzero(res,sizeof(res));
res[0]='.';
if ((n=read(sockfd,request,512))>0)
{
request[n]='\0';
//get request resource string
//find resource
//run cgi task
//response cgi result in form
of HTTP
/* 1 */
while(
(tempChar=request[charIndex])!='\n')
{
if(tempChar!=' ')
token[
charIndexInToken ]=tempChar;
else
{
token[charIndexInToken]='\0';
if(!strcmp(token,"GET"))
{
bzero(token,strlen(token));
charIndexInToken=-1;
}
else
{
break;
}
}
charIndex++;
charIndexInToken++;
}
strcat(res,token);
/* 2 */
FILE
*fp=popen(res,"r");
char cgiResult[512];
int
numRead=fread(cgiResult,1,512,fp);
cgiResult[numRead]='\0';
/* 3 */
char
response[512]="HTTP/1.1 200 OK\nServer: aga-j\n";
strcat(response,cgiResult);
printf("%s",response);
write(sockfd,response,strlen(response));
}
}
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<sys/types.h>
2 #include<sys/socket.h>
3 #include<strings.h>
4 #include<arpa/inet.h>
5 #include<unistd.h>
6 #include<stdlib.h>
7 #include<stdio.h>
8 #include<string.h>
9 #include<errno.h>
10 #include<signal.h>
11 #include<sys/wait.h>
12 #include<pthread.h>
13
14 #define LISTEN_PORT 84
15 void cgi_echo(int sockfd)
16 {
17 ssize_t n;
18 char request[512];
19 char tempChar;
20 int charIndex=0;
21 char token[128];
22 int charIndexInToken=0;
23 char res[128];
24 bzero(res,sizeof(res));
25 res[0]='.';
26
27 if ((n=read(sockfd,request,512))>0)
28 {
29 request[n]='\0';
30 //get request resource string
31 //find resource
32 //run cgi task
33 //response cgi result in form of HTTP
34
35 /* 1 */
36 while( (tempChar=request[charIndex])!='\n')
37 {
38 if(tempChar!=' ')
39 token[ charIndexInToken ]=tempChar;
40 else
41 {
42 token[charIndexInToken]='\0';
43 if(!strcmp(token,"GET"))
44 {
45 bzero(token,strlen(token));
46 charIndexInToken=-1;
47 }
48 else
49 {
50 break;
51 }
52 }
53 charIndex++;
54 charIndexInToken++;
55 }
56 strcat(res,token);
57
58 /* 2 */
59 FILE *fp=popen(res,"r");
60 char cgiResult[512];
61 int numRead=fread(cgiResult,1,512,fp);
62 cgiResult[numRead]='\0';
63
64
65 /* 3 */
66 char response[512]="HTTP/1.1 200 OK\nServer: aga-j\n";
67 strcat(response,cgiResult);
68 printf("%s",response);
69 write(sockfd,response,strlen(response));
70 }
71 }
72
73 int main(int argc, char **argv)
74 {
75 int listenfd, connfd;
76 pid_t childpid;
77 socklen_t chilen;
78
79 struct sockaddr_in chiaddr,servaddr;
80
81 listenfd=socket(AF_INET,SOCK_STREAM,0);
82 if(listenfd==-1)
83 {
84 printf("socket established error: %s\n",(char*)strerror(errno));
85 }
86
87 bzero(&servaddr,sizeof(servaddr));
88 servaddr.sin_family=AF_INET;
89 servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
90 servaddr.sin_port=htons(LISTEN_PORT);
91
92 int bindc=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
93 if(bindc==-1)
94 {
95 printf("bind error: %s\n",strerror(errno));
96 }
97
98 listen(listenfd,5); //limit是SOMAXCONN
99 //客户connect后会在队列中等待accept取出
100
101 for(;;)
102 {
103 chilen=sizeof(chiaddr);
104
105 connfd=accept(listenfd,(struct sockaddr*)&chiaddr,&chilen);
106 //阻塞在accept,直到三次握手成功了才返回
107 if(connfd==-1)
108 printf("accept client error: %s\n",strerror(errno));
109 else
110 printf("client connected\n");
111
112
113 printf("client from %s\n",inet_ntoa(chiaddr.sin_addr));
114
115 cgi_echo(connfd);
116
117 close(connfd);
118
119 }
120 }
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步