注:这是在linux下编写的一个基于B/S模型的浏览器和服务器之间的通信http服务器项目,底层用的是面向链接、可靠的字节流传输的TCP协议,同时用了sock编程,多线程编程,管道,CGI


  1 
  2 
  3 #include"http.h"
  4 
  5 void print_debug(const char * msg)
  6 {
  7 #ifdef _DEBUG_
  8         printf("%s\n", msg);
  9 #endif
 10 }
 11 
 12 void usage(const char *proc)
 13 {
 14     printf("Usage : %s [PORT]\n", proc);
 15 }
 16 
 17 void print_log(const char *fun, int line, int err_no,  const char *err_str)
 18 {
 19     printf("[%s: %d] [%d] [%s]\n", fun, line, err_no, err_str);
 20 }
 21 static void bad_request(int client)
 22 {
 23     print_debug("enter our fault...\n");
 24     char buf[1024];
 25     sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
 26     send(client, buf, strlen(buf), 0);
 27     sprintf(buf, "Content-type: text/html\r\n");
 28     send(client, buf, strlen(buf), 0);
 29     sprintf(buf, "\r\n");
 30     send(client, buf, strlen(buf), 0);
 31     sprintf(buf, "<html></br><p>your enter message is a bad request</p></br></html>\r\n");
 32     send(client, buf, strlen(buf), 0);
 33 }
 34 //清理头部信息
 35 void clear_header(int client)
 36 {
 37     char buf[1024];
 38     memset(buf, '\0', sizeof(buf));
 39     int ret = 0;
 40     do{
 41         ret = get_line(client, buf, sizeof(buf));
 42     }while(ret > 0 && strcmp(buf, "\n") != 0 );
 43 }
 44 //从sock里取出字符存放到buf里
 45 int get_line(int sock, char *buf, int max_len)
 46 {
 47     if( !buf || max_len < 0)
 48         return -1;
 49 
 50     int i = 0;
 51     int n = 0;
 52     char ch = '\0';
 53     while( i < max_len-1 && ch != '\n' ){
 54         n = recv(sock, &ch, 1, 0);
 55         if(n > 0){//success
 56             if( ch == '\r' ){
 57                 n = recv(sock, &ch, 1,MSG_PEEK);//窥探方式取字符
 58                 if( n > 0 && ch == '\n' ){//windows
 59                     recv(sock, &ch, 1, 0);//delete
 60                  }else{
 61                      ch = '\n';
 62                     }
 63             }
 64             buf[i++] = ch;
 65         }else{//failed
 66             ch = '\n';
 67         }
 68     }
 69     buf[i] = '\0';
 70     return i;
 71 }
 72 
 73 void echo_error_to_client(int client, int error_code)
 74 {
 75     switch(error_code){
 76         case 400://request error
 77                 bad_request(client);
 78                 break;
 79         case 404://not found
 80     //          not_found(client);
 81                 break;
 82         case 500://server error
 83         //      server_error(client);
 84                 break;
 85         case 503://server unavailable
 86     //          server_unavailable(client);
 87                 break;
 88         default:
 89     //          default_error(client);
 90                 break;
 91                 
 92     }
 93 }
 94 void echo_html(int client, const char *path, unsigned int file_size)
 95 {
 96     if( !path ){
 97     return;
 98     }
 99     int in_fd = open(path, O_RDONLY);
100     if(in_fd < 0){
101         print_debug("open index.html error");
102         return;
103         }
104     print_debug("open index.html success");
105     char echo_line[1024];
106     memset(echo_line, '\0', sizeof(echo_line));
107     strncpy(echo_line, HTTP_VERSION, strlen(HTTP_VERSION)+1);
108     strcat(echo_line, " 200 OK");
109     strcat(echo_line, "\r\n\r\n");
110     send(client, echo_line,strlen(echo_line), 0);
111     print_debug("send echo head success");
112     if( sendfile(client, in_fd, NULL, file_size) < 0 ){
113     print_debug("send_file error");
114     //echo_error_to_client();
115     close(in_fd);
116     return;
117     }
118     print_debug("sendfile success");
119     close(in_fd);
120 }
121 //请求链接函数
122 void *accept_request(void *arg)
123 {
124     printf("accept_request\n");
125     pthread_detach(pthread_self());//detach让线程分离
126     int sock_client = (int)arg;
127     
128     int  cgi = 0;
129     char *query_string = NULL;  //接受资源后的参数
130     char method[_COMM_SIZE_/10];//方法
131     char url[_COMM_SIZE_];      //资源
132     char buffer[_COMM_SIZE_];   //存放从浏览器里取得的所以字符
133     char path[_COMM_SIZE_];     //运行路径
134 
135     memset(method, '\0', sizeof(method));
136     memset(url, '\0', sizeof(url));
137     memset(buffer, '\0', sizeof(buffer));
138     memset(path, '\0', sizeof(path));
139 
140     if(get_line(sock_client, buffer, sizeof(buffer)) < 0){
141             return NULL;
142     }
143     int i = 0; 
144     int j = 0;//buffer line index
145     //isspace判断是否为空格,如果是空格,返回1,否则返回0
146     while( !isspace(buffer[j]) && i < sizeof(method)-1 && j < sizeof(buffer)-1){
147             method[i] = buffer[j];
148             i++, j++;
149     }
150     //忽略大小写比较
151     if( strcasecmp(method, "GET") && strcasecmp(method, "POST")){
152         return NULL;
153     }
154     while( isspace(buffer[j]) && j < sizeof(buffer)){
155             j++;
156     }
157     i = 0;
158     while(!isspace(buffer[j]) && i < sizeof(url)-1 && j < sizeof(buffer)){
159         url[i] = buffer[j];
160         i++;j++;
161     }
162     print_debug(method);
163     print_debug(url);
164 
165     if(strcasecmp(method, "POST") == 0)
166         cgi = 1;
167     if(strcasecmp(method, "GET") == 0){
168         query_string = url;
169         while( *query_string != '?' && *query_string != '\0'){
170             query_string++;
171         }
172         if( *query_string == '?' ){//url = /XXX/XXX + arg
173             *query_string = '\0';
174             query_string++;
175             cgi = 1;
176         }
177     }
178     sprintf(path, "htdocs%s", url);
179     if(path[strlen(path)-1] == '/'){
180     strcat(path, MAIN_PAGE);
181     }
182     print_debug(path);
183 
184     struct stat st;
185     if( stat(path, &st) < 0 ){ //failed, does not exist
186         print_debug("miss cgi");
187         clear_header(sock_client);
188       }else{
189           if(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)
190                 cgi = 1;
191 
192           if(cgi)
193             exe_cgi(sock_client, path, method, query_string);
194           else{
195                 clear_header(sock_client);
196                 print_debug("begin enter our echo_html");
197                 echo_html(sock_client, path, st.st_size);
198             }
199     }
200     close(sock_client);
201     return NULL;
202 }
203 //执行cgi函数
204 void exe_cgi(int sock_client, const char *path, const char *method,const char *query_string)
205 {
206     print_debug("enter cgi");
207     char buf[_COMM_SIZE_];
208     int numchars = 0;
209     int content_length = -1;
210             
211     int cgi_input[2] = {0, 0};
212     int cgi_output[2] = {0, 0};
213                                 
214     pid_t id;
215     print_debug(method);
216 
217     if(strcasecmp(method, "GET") == 0){//GET
218         clear_header(sock_client);
219         }else{//POST
220     do{
221         memset(buf, '\0', sizeof(buf));
222         numchars = get_line(sock_client, buf, sizeof(buf));
223         if(strncasecmp(buf, "Content-Length:", strlen("Content-Length:")) == 0){
224             content_length = atoi(&buf[16]);
225             }
226         }while(numchars > 0 && strcmp(buf, "\n") != 0);
227         if( content_length == -1 ){
228             //echo_error_to_client();
229             return;
230             }
231         }
232     memset(buf, '\0', sizeof(buf));
233     strcpy(buf, HTTP_VERSION);
234     strcat(buf, " 200 OK\r\n\r\n");
235     //把buf里的数据发送到sock_client里
236     send(sock_client, buf, strlen(buf), 0);
237     //创建cgi输入管道
238     if( pipe(cgi_input) == -1 ){//pipe error//echo_error_to_client();
239         return;
240         }
241     //创建cgi输出管道
242     if( pipe(cgi_output) == -1 ){
243         close(cgi_input[0]);
244         close(cgi_input[1]);//echo_error_to_client();
245         return;
246     }
247     //创建进程
248     if( (id = fork()) < 0){//fork error
249         close(cgi_input[0]);
250         close(cgi_input[1]);
251         close(cgi_output[0]);
252         close(cgi_output[1]);//echo_error_to_client();
253         return;
254     }else if( id == 0 ){//child
255         char query_env[_COMM_SIZE_/10];
256         char method_env[_COMM_SIZE_];
257         char content_len_env[_COMM_SIZE_];
258     
259         memset(method_env, '\0', sizeof(method_env));
260         memset(query_env, '\0', sizeof(query_env));
261         memset(content_len_env, '\0', sizeof(content_len_env));
262         
263         close(cgi_input[1]);
264         close(cgi_output[0]);
265         //将标准输出和标准输入重定向到cgi_input和cgi_output
266         dup2(cgi_input[0], 0);
267         dup2(cgi_output[1], 1);
268         
269         sprintf(method_env, "REQUEST_METHOD=%s", method);
270         putenv(method_env);
271         
272         if(strcasecmp("GET", method) == 0){//POST
273             sprintf(query_env, "QUERY_STRING=%s", query_string);
274             putenv(query_env);
275             }else{//POST
276                 sprintf(content_len_env, "CONTENT_LENGTH=%d", content_length);
277                 putenv(content_len_env);
278             }
279         execl(path, path, NULL);
280         exit(1);
281     }else{//father
282         close(cgi_input[0]);
283         close(cgi_output[1]);
284         int i = 0;
285         char c = '\0';
286         if(strcasecmp("POST", method) == 0){
287             for(; i < content_length; i++ ){
288                 recv(sock_client, &c, 1, 0);//从sock_client里取出一个字符并存入c里
289                 write(cgi_input[1], &c, 1); //从c里取出一个字符发送到cgi_input[1]里,即标准输入里
290             }
291         }
292         while( read(cgi_output[0], &c, 1) > 0 ){
293             send(sock_client, &c, 1, 0);//把c里字符send到sock_client中
294         }
295         
296         close(cgi_input[1]);
297         close(cgi_output[0]);
298         
299         waitpid(id, NULL, 0);//防止僵尸进程产生
300     }
301 }
302 
303 //创建端口,并初始化
304 int start(short port)
305 {
306     int listen_sock = socket(AF_INET, SOCK_STREAM, 0);//创建TCP协议的sock(SOCK_STREAM:字节流)
307     if(listen_sock == -1){
308         print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
309         exit(1);
310     }
311     //reuse port
312     int flag = 1;
313     setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));//端口复用
314 
315     //初始化sock函数家族
316     struct sockaddr_in local;
317     local.sin_family = AF_INET;
318     local.sin_port   = htons(port);//host -> net
319     local.sin_addr.s_addr   = htonl(INADDR_ANY);
320     socklen_t len = sizeof(local);
321     //绑定端口号,成功返回0,失败返回-1
322     if(bind(listen_sock, (struct sockaddr*)&local, len) == -1){
323         print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
324         exit(2);
325     }
326     //监听端口号
327     if(listen(listen_sock, _BACK_LOG_) == -1){
328         print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
329         exit(3);
330     }
331     return listen_sock; //sucess
332 }
333 //得到一个随机端口号
334 short get_port(short port)
335 {
336     if(port==0)
337     {
338         socklen_t length=sizeof(local);
339         if(getsockname(listen_sock,(struct sockaddr*)&local,&length)==-1)
340         {
341             print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
342             exit(1);
343         }
344         port=ntohs(local.sin_port);
345 //      printf("port is %d\n",port);
346     }
347     return port;
348 }
349 int main(int argc,char *argv[])
350 {
351     short port=0;
352     if(argc >1)
353          port=atoi(argv[1]);
354     else
355         port=get_port(port);//得到一个随机的端口号
356 
357     int sock=start(port);
358     printf("***start success sock:%d ***\n",sock);
359     struct sockaddr_in client;
360     socklen_t len=0;
361     int count=1;//线程链接个数
362     char buf[4096];
363     memset(buf,'\0',sizeof(buf));
364     while(1)
365     {
366         //阻塞式等待客服端链接
367         int new_sock=accept(sock,(struct sockaddr*)&client,&len);
368 //      recv(new_sock,buf,sizeof(buf),0);
369 //      printf("buf:%s\n",buf);
370         if(new_sock<0)
371         {
372             print_log(__FUNCTION__, __LINE__, errno, strerror(errno));
373             continue;
374         }
375         pthread_t new_thread;
376         //创建链接来处理客服端链接
377         pthread_create(&new_thread,NULL,accept_request,(void*)new_sock);
378         printf("get %d new connection\n",count++);
379     }
380     return 0;
381 }   

2:http.h


 #ifndef _HTTP_H
  2 #define _HTTP_H
  3 
  4 #include<stdio.h>
  5 #include<stdlib.h>
  6 #include<unistd.h>
  7 #include<sys/types.h>
  8 #include<sys/socket.h>
  9 #include<netinet/in.h>
 10 #include<arpa/inet.h>
 11 #include<errno.h>
 12 #include<pthread.h>
 13 #include<string.h>
 14 #include<sys/sendfile.h>
 15 #include<sys/stat.h>
 16 #include<fcntl.h>
 17 
 18 
 19 #define _BACK_LOG_ 5
 20 #define _COMM_SIZE_ 1024
 21 #define MAIN_PAGE "index.html"
 22 #define HTTP_VERSION "HTTP/1.1"
 23 
 24 void print_debug(const char * msg);
 25 void usage(const char *proc);
 26 void print_log(const char *fun, int line, int err_no,  const char *err_str);
 27 static void bad_request(int client);
 28 void clear_header(int client);
 29 int get_line(int sock, char *buf, int max_len);
 30 void echo_error_to_client(int client, int error_code);
 31 void echo_html(int client, const char *path, unsigned int file_size);
 32 void *accept_request(void *arg);
 33 void exe_cgi(int sock_client, const char *path, const char *method,const char *query_string);
 34 int start(short  port);
 35 #endif           

Logo

更多推荐