Linux_C socket 服务器(cat ,execl功能)

客户端和web服务器交互的基本结构如下:

  (1)客户端发送请求

            GET filename HTTP/version

     可选参数

     空行

 (2)服务器发送应答

             HTTP/version status-code status-message

             附加信息

             空行

              内容

webserv.c


  1 /* webserv.c    a minimal web server (version 0.2)
  2  * usage : webserv portnumber
  3  */
  4 #include <stdio.h>
  5 #include <sys/types.h>
  6 #include <sys/socket.h>
  7 #include <string.h>
  8 #include <sys/stat.h>
  9 #include <netinet/in.h>
 10 #include <netdb.h>
 11 
 12 /**************************************************************
 13  * 下面I/O的函数一些总结:
 14  *   1.可以在参数列表中指定并可以格式化输出函数: fprintf();
 15  *          eg:fprintf(stderr, "usage: webserv portnum");
 16  *             fprintf((FILE*)fp, "HTTP/1.0 200 OK
");
 17  *   2.从指定FILE指针中读取一字符串, char* fgets(char* buf, int size, FILE* stream)
 18  *          buf为保存字符的内存空间,直到出现换行、文件尾、已读了size-1个字符为止,最后加NULL作为字符串结束。返回NULL表示出错。
 19  *          eg:fgets(request, BUFSIZ, fpin);
 20  *   3.读一个字符,写一个字符。 eg: while((c=getc(fpfile))!=EOF) putc(c, fpsock);
 21  *************************************************************/
 22 
 23 
 24 int make_server_socket(int);
 25 
 26 int main(int argc, char* argv[]) {
 27   int sock, fd;
 28   FILE * fpin;
 29   char request[BUFSIZ];
 30   
 31   if(argc==1) {
 32     fprintf(stderr , "usage :webserv portnum.
");
 33     exit(1);
 34   }
 35   sock=make_server_socket(atoi(argv[1]));
 36   if(sock==-1)
 37     exit(2);
 38   while(1) {
 39     /* take a call and buffer it */
 40     fd=accept(sock, NULL, NULL);
 41     fpin=fdopen(fd, "r");
 42     /* read request */
 43     fgets(request, BUFSIZ, fpin);
 44     printf("get a call ; request = %s", request);
 45     read_til_crnl(fpin);
 46     /* do what client asks */
 47     process_rq(request, fd);
 48     
 49     fclose(fpin);
 50   }
 51   return 0;
 52 }
 53 
 54 int make_server_socket(int portnum) {
 55   int sock;
 56   struct sockaddr_in saddr;
 57   struct hostent *hp;
 58   char hostname[BUFSIZ];
 59   
 60   sock=socket(PF_INET, SOCK_STREAM, 0);
 61   if(sock==-1)
 62     return -1;
 63   
 64   gethostname(hostname, BUFSIZ);
 65   hp=gethostbyname(hostname);
 66   bzero(&saddr, sizeof(saddr));
 67   bcopy((void*)hp->h_addr, (void*)&saddr.sin_addr, hp->h_length);
 68   saddr.sin_port=htons(portnum);
 69   saddr.sin_family=AF_INET;
 70   
 71   if(bind(sock, (struct sockaddr*)&saddr, sizeof(saddr))!=0)
 72     return -1;
 73   if(listen(sock ,1)!=0)
 74     return -1;
 75   return sock;
 76 }
 77 
 78 /**********************************************************************
 79  * read_til_crnl(FILE* )
 80  * skip over all request info until a CRNL is seen
 81  *********************************************************************/
 82 read_til_crnl(FILE* fp) {
 83   char buf[BUFSIZ];
 84   while(fgets(buf, BUFSIZ, fp)!=NULL && strcmp(buf, "
")!=0)
 85     ;
 86 }
 87 
 88 /*********************************************************************
 89  * process_rq(char* rq, int fd)
 90  * do what the request asks for and write reply to fd
 91  * handles request in a new process
 92  * rq is HTTP command: GET /foo/bar.html HTTP/1.0
 93  ********************************************************************/
 94 process_rq(char *rq, int fd) {
 95   char cmd[BUFSIZ], arg[BUFSIZ];
 96   /*creat a new process and return if not the child*/
 97   if(fork()!=0) 
 98     return ;
 99   strcpy(arg, "./");   /* precede args with .*/
100   if(sscanf(rq, "%s %s", cmd, arg+2)!=2)
101     return ;
102   if(strcmp(cmd, "GET")!=0)
103     cannot_do(fd);
104   else if(not_exist(arg))
105     do_404(arg, fd);
106   else if(isadir(arg))
107     do_ls(arg, fd);
108   else if(ends_in_cgi(arg))
109     do_exec(arg, fd);
110   else 
111     do_cat(arg, fd);
112 }
113 
114 
115 
116 /*********************************************************
117  * the reply header thing: all functions need one
118  * if content_type is NULL then don't send content type
119  ********************************************************/
120 header(FILE* fp, char * content_type) {
121   fprintf(fp, "HTTP/1.0 200 OK
");
122   if(content_type)
123     fprintf(fp, "Content-type: %s
", content_type);
124 }
125 
126 /********************************************************* 
127  * simple functions first:
128  *   cannot_do(fd): unimplemented HTTP command
129  * and  do_404*(item ,fd)  no such object
130  ********************************************************/
131 
132 cannot_do(fd) {
133   FILE *fp=fdopen(fd, "w");
134   
135   fprintf(fp, "HTTP/1.0 501 Not Implemented
");
136   fprintf(fp, "Content-type: text/plain
");
137   fprintf(fp, "
");
138   
139   fprintf(fp, "That command is not yet implemented
");
140   fclose(fp);
141 }
142 
143 do_404(char* item, int fd) {
144   FILE* fp=fdopen(fd, "w");
145   
146   fprintf(fp, "HTTP/1.0 404 Not Found
");
147   fprintf(fp, "Content-type:text/plain
");
148   fprintf(fp, "
" );
149   
150   fprintf(fp, "The item you requested: %s
is not found
", item);
151   fclose(fp);
152 }
153 
154 /******************************************************************
155  * the directory listing sectoin
156  * isadir() uses stat, not_exist() uses stat
157  * do_ls runs ls. It should not
158  *****************************************************************/
159 
160 isadir(char *f) {
161   struct stat info;
162   return (stat(f, &info)!=-1 && S_ISDIR(info.st_mode));
163 }
164 
165 not_exist(char *f) {
166   struct stat info;
167   return (stat(f, &info)==-1);
168 }
169 
170 do_ls(char *dir, int fd) {
171   FILE* fp;
172   fp=fdopen(fd, "w");
173   header(fp, "text/plain");
174   fprintf(fp, "
");
175   fflush(fp);
176   
177   dup2(fd, 1);   /*注意这里的文件标识符为fd,即是socket单位,而不是fdopen()的返回值,切记切记*/
178   dup2(fd, 2);
179   close(fd);
180   execlp("ls", "ls", "-l",dir, NULL);
181   perror(dir);
182   exit(1);
183 }
184 
185 /*******************************************************
186  * the cgi stuff function to check extension and
187  * one to run the program.
188  ******************************************************/
189 char * file_type(char* f) {
190   /* returns 'extension' of file */
191   char *cp;
192   if((cp=strrchr(f, '.'))!=NULL)
193     return cp+1;
194   return "";
195 }
196 
197 ends_in_cgi(char* f) {
198   return (strcmp(file_type(f) , "cgi")==0);
199 }
200 
201 do_exec(char *prog, int fd) {
202   FILE *fp;
203   
204   fp=fdopen(fd, "w");
205   header(fp, NULL);
206   fflush(fp);
207   dup2(fd, 1);
208   dup2(fd, 2);
209   close(fd);
210   execl(prog, prog, NULL);
211   perror(prog);
212 }
213 
214 /*******************************************************************
215  * do_cat(filename, fd)
216  * sends back contents after a header
217  ******************************************************************/
218 do_cat(char* f, int fd) {
219   char *extension = file_type(f);
220   char *content = "text/plain";
221   FILE *fpsock, *fpfile;
222   int c;
223   
224   if(strcmp(extension, "html")==0)
225     content = "text/html";
226   else if(strcmp(extension, "gif")==0)
227     content = "image/gif";
228   else if(strcmp(extension, "jpg")==0)
229     content = "image/jpeg";
230   else if(strcmp(extension, "jpeg")==0)
231     content = "image/jpeg";
232 
233   fpsock = fdopen(fd, "w");
234   fpfile = fopen(f, "r");
235   if(fpsock != NULL && fpfile!=NULL) {
236     header(fpsock, content);
237     fprintf(fpsock, "
");
238     while((c=getc(fpfile))!=EOF)
239       putc(c, fpsock);
240     fclose(fpfile);
241     fclose(fpsock);
242   }
243   exit(0);
244 }

用法说明: gcc webserv.c -o webserv

 ./webserv 12345(端口号)

然后在浏览器中输入主机名加端口号可访问

 注意:在do_ls()中,dup2(sock_in, 1),dup2(sock_in,2),一定要注意这一点,重定向的应该是sockid而不是fin=fdopen(sockid,"w").

原文地址:https://www.cnblogs.com/wizzhangquan/p/4095160.html