代码改变世界

Linux TCP server系列(8)-cgi server

2011-09-18 21:36  Aga.J  阅读(1267)  评论(0编辑  收藏  举报

目标:

单进程cgi服务器

 

CGI介绍:

  CGI使在网络服务器运行外部分应用程序(或网关)成为可能。CGI-BIN 目录是存放CGI脚本的地方。这些脚本使WWW服务器和浏览器能运行外部程序,而无需启动另一个原因程序。 CGI是在HTTP服务器下运行外部程序(或网关)的一个接口,它能让网络用户访问远程系统上的使用类型程序,就好像他们在实际使用那些远程计算机一样。

  尽管CGI易于使用,但是当大批人同时使用一个CGI应用程序是会反应较慢,网络服务器 速度也会受到很大 影响。CGI应用程序的优点是可以独立运行。

CGI应用程序可以由大多数的编程语言编写,如PerlPractical Extraction and Report Language)C\C++Java Visual Basic等。不过对于那些没有太多编程经验的网页制作人来说,实在是一个不小的难题。

 

CGI应用程序的工作原理:

  1.浏览器通过HTML表单或超链接请求指上一个CGI应用程序的URL

  2.服务器收发到请求。

  3.服务器执行指定所CGI应用程序。

  4.CGI应用程序执行所需要的操作,通常是基于浏览者输入的内容。

  5.CGI应用程序把结果格式化为网络服务器和浏览器能够理解的文档(通常是HTML网页)。

6.网络服务器把结果返回到浏览器中。

 

有关CGI应用程序要注意的:

  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));

         }

}

代码:

server.cpp
  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 }